From 9167b76283feda7064626ecb368613538f3911b4 Mon Sep 17 00:00:00 2001
From: kmochocki <kmochocki@syncad.com>
Date: Fri, 20 Sep 2024 16:23:55 +0000
Subject: [PATCH] Move all apis to one directory in _interfaces

---
 .gitlab-ci.yml                                |  15 +-
 beekeepy/beekeepy/__init__.py                 |  25 +-
 beekeepy/beekeepy/_apis/__init__.py           |  30 ++
 beekeepy/beekeepy/_apis/abc/__init__.py       |  33 +++
 .../{_remote_handle => _apis}/abc/api.py      |  15 +-
 .../abc/api_collection.py                     |  11 +-
 beekeepy/beekeepy/_apis/abc/sendable.py       |  21 ++
 .../api => _apis/abc}/session_holder.py       |   7 +-
 .../beekeepy/_apis/app_status_api/__init__.py |  19 ++
 .../_apis/app_status_api/api_collection.py    |  32 +++
 .../_apis/app_status_api/async_api.py         |  12 +
 .../beekeepy/_apis/app_status_api/sync_api.py |  12 +
 .../api => _apis}/apply_session_token.py      |   3 +-
 .../beekeepy/_apis/beekeeper_api/__init__.py  |  14 +
 .../_apis/beekeeper_api/api_collection.py     |  32 +++
 .../api => _apis/beekeeper_api}/async_api.py  |  21 +-
 .../beekeeper_api}/beekeeper_api_commons.py   |   4 +-
 .../api => _apis/beekeeper_api}/sync_api.py   |  22 +-
 beekeepy/beekeepy/_communication/__init__.py  |  28 ++
 .../_communication/abc/communicator.py        |   8 +-
 .../abc/http_server_observer.py               |  17 --
 .../abc/notification_handler.py               |  21 --
 .../beekeepy/_communication/abc/overseer.py   |  11 +-
 beekeepy/beekeepy/_communication/abc/rules.py |   2 +-
 .../_communication/aiohttp_communicator.py    |   4 +-
 .../appbase_notification_handler.py           |  40 ---
 .../beekeepy/_communication/async_server.py   | 104 -------
 .../_communication/httpx_communicator.py      |   4 +-
 .../_communication/notification_decorator.py  |  43 ---
 .../_communication/request_communicator.py    |   4 +-
 beekeepy/beekeepy/_communication/settings.py  |   2 +-
 .../universal_notification_server.py          |  87 ------
 .../{_interface => _communication}/url.py     |  11 +
 beekeepy/beekeepy/_executable/__init__.py     |  24 +-
 beekeepy/beekeepy/_executable/abc/__init__.py |  16 ++
 .../beekeepy/_executable/abc/arguments.py     | 144 ++++++++++
 .../{_interface => _executable/abc}/config.py |  50 +++-
 .../_executable/{ => abc}/executable.py       | 109 ++++++--
 .../beekeepy/_executable/{ => abc}/streams.py |   2 +-
 .../_executable/arguments/arguments.py        |  64 -----
 .../{arguments => }/beekeeper_arguments.py    |   9 +-
 .../beekeepy/_executable/beekeeper_config.py  |   5 +-
 .../_executable/beekeeper_executable.py       |  43 +--
 beekeepy/beekeepy/_executable/defaults.py     |   3 +-
 beekeepy/beekeepy/_interface/__init__.py      |  24 ++
 beekeepy/beekeepy/_interface/abc/__init__.py  |  23 ++
 .../_interface/abc/asynchronous/beekeeper.py  |  18 +-
 .../_interface/abc/asynchronous/session.py    |   2 +-
 .../_interface/abc/asynchronous/wallet.py     |   2 +-
 .../beekeepy/_interface/abc/packed_object.py  |  19 +-
 .../_interface/abc/synchronous/beekeeper.py   |  18 +-
 .../_interface/abc/synchronous/session.py     |   2 +-
 .../_interface/abc/synchronous/wallet.py      |   2 +-
 .../_interface/asynchronous/beekeeper.py      |  41 +--
 .../_interface/asynchronous/session.py        |   9 +-
 .../_interface/asynchronous/wallet.py         |  11 +-
 beekeepy/beekeepy/_interface/settings.py      |   7 +
 .../_interface/synchronous/beekeeper.py       |  41 +--
 .../_interface/synchronous/session.py         |   9 +-
 .../beekeepy/_interface/synchronous/wallet.py |   9 +-
 .../beekeepy/_interface/wallets_common.py     |  21 +-
 beekeepy/beekeepy/_remote_handle/__init__.py  |  30 ++
 .../_remote_handle/{ => abc}/batch_handle.py  |  12 +-
 .../beekeepy/_remote_handle/abc/handle.py     |  44 +--
 .../beekeepy/_remote_handle/api/__init__.py   |   6 -
 .../_remote_handle/api/api_collection.py      |  39 ---
 .../_remote_handle/app_status_probe.py        |  28 ++
 beekeepy/beekeepy/_remote_handle/beekeeper.py |  35 ++-
 beekeepy/beekeepy/_remote_handle/settings.py  |  14 +-
 .../beekeepy/_runnable_handle/__init__.py     |  21 +-
 .../beekeepy/_runnable_handle/beekeeper.py    | 248 ++++-------------
 .../_runnable_handle/beekeeper_callbacks.py   |  42 ---
 .../beekeeper_notification_handler.py         |  70 -----
 .../beekeepy/_runnable_handle/match_ports.py  |  72 +++++
 .../notification_handler_base.py              |  29 --
 .../_runnable_handle/runnable_handle.py       | 256 ++++++++++++++++++
 .../beekeepy/_runnable_handle/settings.py     |  18 +-
 .../arguments => _utilities}/__init__.py      |   0
 .../build_json_rpc_call.py                    |   0
 .../{_interface => _utilities}/context.py     |   0
 .../context_settings_updater.py               |   4 +-
 .../{_interface => _utilities}/delay_guard.py |   2 +-
 .../error_logger.py                           |   4 +-
 .../{_interface => _utilities}/key_pair.py    |   0
 .../_sanitize.py => _utilities/sanitize.py}   |   0
 .../settings_holder.py                        |   2 +-
 .../state_invalidator.py                      |   0
 .../{_interface => _utilities}/stopwatch.py   |   2 +-
 .../suppress_api_not_found.py}                |   2 +-
 beekeepy/beekeepy/exceptions/__init__.py      |  18 +-
 beekeepy/beekeepy/exceptions/base.py          |  10 +-
 beekeepy/beekeepy/exceptions/common.py        |  18 +-
 beekeepy/beekeepy/exceptions/executable.py    |  23 ++
 beekeepy/beekeepy/exceptions/overseer.py      |   4 +-
 beekeepy/beekeepy/handle/remote.py            |  24 +-
 beekeepy/beekeepy/handle/runnable.py          |  23 +-
 beekeepy/beekeepy/interfaces.py               |  26 +-
 beekeepy/poetry.lock                          |  37 ++-
 beekeepy/pyproject.toml                       |   6 +-
 hive                                          |   2 +-
 .../handle/api_tests/test_api_close.py        |  12 +-
 .../api_tests/test_api_close_session.py       |   6 +-
 .../api_tests/test_api_create_session.py      |   8 +-
 .../handle/api_tests/test_api_set_timeout.py  |   4 +-
 .../beekeepy_test/handle/basic/test_wallet.py |   4 +-
 .../patterns/config.ini                       |   2 +-
 .../patterns/generate_help_pattern.py         |   5 +-
 .../patterns/help_pattern.txt                 |   6 +-
 .../application_options/test_backtrace.py     |   9 +-
 .../test_default_values.py                    |   1 -
 .../test_export_keys_wallet.py                |   4 +-
 .../application_options/test_log_json_rpc.py  |   2 +-
 .../test_notifications_endpoint.py            |  37 ---
 .../test_unlock_timeout.py                    |   4 +-
 .../application_options/test_wallet_dir.py    |   2 +-
 .../test_webserver_http_endpoint.py           |  17 +-
 .../test_webserver_thread_pool_size.py        |   2 +-
 .../handle/commandline/conftest.py            |   6 +-
 tests/beekeepy_test/handle/conftest.py        |   4 +-
 .../handle/storage/test_storage.py            |  59 +---
 .../handle/various/test_blocking_unlock.py    |  16 +-
 tests/beekeepy_test/interface/test_setup.py   |  16 +-
 .../interface/test_standalone_beekeeper.py    |   6 +-
 tests/conftest.py                             |  18 +-
 .../beekeepy/account_credentials.py           |   2 +-
 .../local_tools/beekeepy/network.py           |  11 +-
 tests/local-tools/poetry.lock                 |  50 +++-
 tests/local-tools/pyproject.toml              |   1 +
 128 files changed, 1600 insertions(+), 1326 deletions(-)
 create mode 100644 beekeepy/beekeepy/_apis/__init__.py
 create mode 100644 beekeepy/beekeepy/_apis/abc/__init__.py
 rename beekeepy/beekeepy/{_remote_handle => _apis}/abc/api.py (93%)
 rename beekeepy/beekeepy/{_remote_handle => _apis}/abc/api_collection.py (52%)
 create mode 100644 beekeepy/beekeepy/_apis/abc/sendable.py
 rename beekeepy/beekeepy/{_remote_handle/api => _apis/abc}/session_holder.py (88%)
 create mode 100644 beekeepy/beekeepy/_apis/app_status_api/__init__.py
 create mode 100644 beekeepy/beekeepy/_apis/app_status_api/api_collection.py
 create mode 100644 beekeepy/beekeepy/_apis/app_status_api/async_api.py
 create mode 100644 beekeepy/beekeepy/_apis/app_status_api/sync_api.py
 rename beekeepy/beekeepy/{_remote_handle/api => _apis}/apply_session_token.py (76%)
 create mode 100644 beekeepy/beekeepy/_apis/beekeeper_api/__init__.py
 create mode 100644 beekeepy/beekeepy/_apis/beekeeper_api/api_collection.py
 rename beekeepy/beekeepy/{_remote_handle/api => _apis/beekeeper_api}/async_api.py (90%)
 rename beekeepy/beekeepy/{_remote_handle/api => _apis/beekeeper_api}/beekeeper_api_commons.py (86%)
 rename beekeepy/beekeepy/{_remote_handle/api => _apis/beekeeper_api}/sync_api.py (81%)
 delete mode 100644 beekeepy/beekeepy/_communication/abc/http_server_observer.py
 delete mode 100644 beekeepy/beekeepy/_communication/abc/notification_handler.py
 delete mode 100644 beekeepy/beekeepy/_communication/appbase_notification_handler.py
 delete mode 100644 beekeepy/beekeepy/_communication/async_server.py
 delete mode 100644 beekeepy/beekeepy/_communication/notification_decorator.py
 delete mode 100644 beekeepy/beekeepy/_communication/universal_notification_server.py
 rename beekeepy/beekeepy/{_interface => _communication}/url.py (88%)
 create mode 100644 beekeepy/beekeepy/_executable/abc/__init__.py
 create mode 100644 beekeepy/beekeepy/_executable/abc/arguments.py
 rename beekeepy/beekeepy/{_interface => _executable/abc}/config.py (59%)
 rename beekeepy/beekeepy/_executable/{ => abc}/executable.py (62%)
 rename beekeepy/beekeepy/_executable/{ => abc}/streams.py (98%)
 delete mode 100644 beekeepy/beekeepy/_executable/arguments/arguments.py
 rename beekeepy/beekeepy/_executable/{arguments => }/beekeeper_arguments.py (82%)
 create mode 100644 beekeepy/beekeepy/_interface/settings.py
 rename beekeepy/beekeepy/_remote_handle/{ => abc}/batch_handle.py (94%)
 delete mode 100644 beekeepy/beekeepy/_remote_handle/api/__init__.py
 delete mode 100644 beekeepy/beekeepy/_remote_handle/api/api_collection.py
 create mode 100644 beekeepy/beekeepy/_remote_handle/app_status_probe.py
 delete mode 100644 beekeepy/beekeepy/_runnable_handle/beekeeper_callbacks.py
 delete mode 100644 beekeepy/beekeepy/_runnable_handle/beekeeper_notification_handler.py
 create mode 100644 beekeepy/beekeepy/_runnable_handle/match_ports.py
 delete mode 100644 beekeepy/beekeepy/_runnable_handle/notification_handler_base.py
 create mode 100644 beekeepy/beekeepy/_runnable_handle/runnable_handle.py
 rename beekeepy/beekeepy/{_executable/arguments => _utilities}/__init__.py (100%)
 rename beekeepy/beekeepy/{_remote_handle => _utilities}/build_json_rpc_call.py (100%)
 rename beekeepy/beekeepy/{_interface => _utilities}/context.py (100%)
 rename beekeepy/beekeepy/{_interface => _utilities}/context_settings_updater.py (93%)
 rename beekeepy/beekeepy/{_interface => _utilities}/delay_guard.py (97%)
 rename beekeepy/beekeepy/{_interface => _utilities}/error_logger.py (93%)
 rename beekeepy/beekeepy/{_interface => _utilities}/key_pair.py (100%)
 rename beekeepy/beekeepy/{_interface/_sanitize.py => _utilities/sanitize.py} (100%)
 rename beekeepy/beekeepy/{_interface => _utilities}/settings_holder.py (97%)
 rename beekeepy/beekeepy/{_interface => _utilities}/state_invalidator.py (100%)
 rename beekeepy/beekeepy/{_interface => _utilities}/stopwatch.py (95%)
 rename beekeepy/beekeepy/{_interface/_suppress_api_not_found.py => _utilities/suppress_api_not_found.py} (96%)
 create mode 100644 beekeepy/beekeepy/exceptions/executable.py
 delete mode 100644 tests/beekeepy_test/handle/commandline/application_options/test_notifications_endpoint.py

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 5daaec37..36922925 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -33,7 +33,7 @@ variables:
 include:
   - project: 'hive/hive'
     # This has to be the same as the commit checked out in the submodule
-    ref: ed702f86d3a1ae567b2d2e1d0d7240a187223964
+    ref: 40e6bc384b63fe116f370153a20c15a46888011f
     file: '/scripts/ci-helpers/prepare_data_image_job.yml'
   - project: 'hive/common-ci-configuration'
     # This should be the same version of Common CI defined in /hive/scripts/ci-helpers/prepare_data_image_job.yml
@@ -96,19 +96,6 @@ prepare_hived_image:
     - public-runner-docker
     - hived-for-tests
 
-prepare_hived_data:
-  extends: .prepare_hived_data_5m
-  needs:
-    - job: prepare_hived_image
-      artifacts: true
-  stage: build
-  variables:
-    SUBMODULE_DIR: "$CI_PROJECT_DIR/hive"
-    BLOCK_LOG_SOURCE_DIR: $BLOCK_LOG_SOURCE_DIR_5M
-    CONFIG_INI_SOURCE: "$CI_PROJECT_DIR/hive/docker/config_5M.ini"
-  tags:
-    - data-cache-storage
-
 build_beekeepy_wheel:
   stage: beekeepy
   extends: .build_wheel_template
diff --git a/beekeepy/beekeepy/__init__.py b/beekeepy/beekeepy/__init__.py
index e416f596..b5402dc7 100644
--- a/beekeepy/beekeepy/__init__.py
+++ b/beekeepy/beekeepy/__init__.py
@@ -1,16 +1,19 @@
 from __future__ import annotations
 
-from beekeepy._interface.abc.asynchronous.beekeeper import Beekeeper as AsyncBeekeeper
-from beekeepy._interface.abc.asynchronous.session import Session as AsyncSession
-from beekeepy._interface.abc.asynchronous.wallet import UnlockedWallet as AsyncUnlockedWallet
-from beekeepy._interface.abc.asynchronous.wallet import Wallet as AsyncWallet
-from beekeepy._interface.abc.packed_object import PackedAsyncBeekeeper, PackedSyncBeekeeper
-from beekeepy._interface.abc.synchronous.beekeeper import Beekeeper
-from beekeepy._interface.abc.synchronous.session import Session
-from beekeepy._interface.abc.synchronous.wallet import UnlockedWallet, Wallet
-from beekeepy._remote_handle.settings import Settings as RemoteHandleSettings
-from beekeepy._runnable_handle.close_already_running_beekeeper import close_already_running_beekeeper
-from beekeepy._runnable_handle.settings import Settings
+from beekeepy._interface import InterfaceSettings as Settings
+from beekeepy._interface.abc import (
+    AsyncBeekeeper,
+    AsyncSession,
+    AsyncUnlockedWallet,
+    AsyncWallet,
+    Beekeeper,
+    PackedAsyncBeekeeper,
+    PackedSyncBeekeeper,
+    Session,
+    UnlockedWallet,
+    Wallet,
+)
+from beekeepy._runnable_handle import close_already_running_beekeeper
 
 __all__ = [
     "AsyncBeekeeper",
diff --git a/beekeepy/beekeepy/_apis/__init__.py b/beekeepy/beekeepy/_apis/__init__.py
new file mode 100644
index 00000000..c9d688e0
--- /dev/null
+++ b/beekeepy/beekeepy/_apis/__init__.py
@@ -0,0 +1,30 @@
+from __future__ import annotations
+
+from beekeepy._apis import abc
+from beekeepy._apis.app_status_api import (
+    AppStatusProbeAsyncApiCollection,
+    AppStatusProbeSyncApiCollection,
+    AsyncAppStatusApi,
+    SyncAppStatusApi,
+)
+from beekeepy._apis.apply_session_token import async_apply_session_token, sync_apply_session_token
+from beekeepy._apis.beekeeper_api import (
+    AsyncBeekeeperApi,
+    BeekeeperAsyncApiCollection,
+    BeekeeperSyncApiCollection,
+    SyncBeekeeperApi,
+)
+
+__all__ = [
+    "abc",
+    "AppStatusProbeAsyncApiCollection",
+    "AppStatusProbeSyncApiCollection",
+    "AsyncAppStatusApi",
+    "SyncAppStatusApi",
+    "SyncBeekeeperApi",
+    "AsyncBeekeeperApi",
+    "BeekeeperSyncApiCollection",
+    "BeekeeperAsyncApiCollection",
+    "sync_apply_session_token",
+    "async_apply_session_token",
+]
diff --git a/beekeepy/beekeepy/_apis/abc/__init__.py b/beekeepy/beekeepy/_apis/abc/__init__.py
new file mode 100644
index 00000000..558e1c1c
--- /dev/null
+++ b/beekeepy/beekeepy/_apis/abc/__init__.py
@@ -0,0 +1,33 @@
+from __future__ import annotations
+
+from beekeepy._apis.abc.api import (
+    AbstractApi,
+    AbstractAsyncApi,
+    AbstractSyncApi,
+    ApiArgumentSerialization,
+    ApiArgumentsToSerialize,
+    HandleT,
+    RegisteredApisT,
+)
+from beekeepy._apis.abc.api_collection import AbstractAsyncApiCollection, AbstractSyncApiCollection
+from beekeepy._apis.abc.sendable import (
+    AsyncSendable,
+    SyncSendable,
+)
+from beekeepy._apis.abc.session_holder import AsyncSessionHolder, SyncSessionHolder
+
+__all__ = [
+    "AbstractApi",
+    "AbstractAsyncApi",
+    "AbstractAsyncApiCollection",
+    "AbstractSyncApi",
+    "AbstractSyncApiCollection",
+    "ApiArgumentSerialization",
+    "ApiArgumentsToSerialize",
+    "AsyncSendable",
+    "AsyncSessionHolder",
+    "HandleT",
+    "RegisteredApisT",
+    "SyncSendable",
+    "SyncSessionHolder",
+]
diff --git a/beekeepy/beekeepy/_remote_handle/abc/api.py b/beekeepy/beekeepy/_apis/abc/api.py
similarity index 93%
rename from beekeepy/beekeepy/_remote_handle/abc/api.py
rename to beekeepy/beekeepy/_apis/abc/api.py
index af7bfc9e..4dcbd68c 100644
--- a/beekeepy/beekeepy/_remote_handle/abc/api.py
+++ b/beekeepy/beekeepy/_apis/abc/api.py
@@ -13,16 +13,11 @@ from typing import (
     ClassVar,
     Generic,
     ParamSpec,
-    TypeAlias,
     TypeVar,
     get_type_hints,
 )
 
-from beekeepy._remote_handle.abc.handle import (
-    AbstractAsyncHandle,
-    AbstractSyncHandle,
-)
-from beekeepy._remote_handle.batch_handle import AsyncBatchHandle, SyncBatchHandle
+from beekeepy._apis.abc.sendable import AsyncSendable, SyncSendable
 from schemas._preconfigured_base_model import PreconfiguredBaseModel
 from schemas.fields.serializable import Serializable
 from schemas.operations.representations.legacy_representation import LegacyRepresentation
@@ -34,9 +29,7 @@ if TYPE_CHECKING:
 
 
 P = ParamSpec("P")
-SyncHandleT: TypeAlias = AbstractSyncHandle[Any] | SyncBatchHandle[Any]
-AsyncHandleT: TypeAlias = AbstractAsyncHandle[Any] | AsyncBatchHandle[Any]
-HandleT = TypeVar("HandleT", bound=SyncHandleT | AsyncHandleT)
+HandleT = TypeVar("HandleT", bound=SyncSendable | AsyncSendable)
 
 RegisteredApisT = defaultdict[bool, defaultdict[str, set[str]]]
 ApiArgumentsToSerialize = tuple[tuple[Any, ...], dict[str, Any]]
@@ -121,7 +114,7 @@ class AbstractApi(ABC, Generic[HandleT]):
         self._owner = owner
 
 
-class AbstractSyncApi(AbstractApi[SyncHandleT]):
+class AbstractSyncApi(AbstractApi[SyncSendable]):
     """Base class for all apis, that provides synchronous endpoints."""
 
     def _additional_arguments_actions(
@@ -153,7 +146,7 @@ class AbstractSyncApi(AbstractApi[SyncHandleT]):
         return impl  # type: ignore[return-value]
 
 
-class AbstractAsyncApi(AbstractApi[AsyncHandleT]):
+class AbstractAsyncApi(AbstractApi[AsyncSendable]):
     """Base class for all apis, that provides asynchronous endpoints."""
 
     async def _additional_arguments_actions(
diff --git a/beekeepy/beekeepy/_remote_handle/abc/api_collection.py b/beekeepy/beekeepy/_apis/abc/api_collection.py
similarity index 52%
rename from beekeepy/beekeepy/_remote_handle/abc/api_collection.py
rename to beekeepy/beekeepy/_apis/abc/api_collection.py
index d8836883..8838e34b 100644
--- a/beekeepy/beekeepy/_remote_handle/abc/api_collection.py
+++ b/beekeepy/beekeepy/_apis/abc/api_collection.py
@@ -2,7 +2,8 @@ from __future__ import annotations
 
 from typing import Generic
 
-from beekeepy._remote_handle.abc.api import AsyncHandleT, HandleT, SyncHandleT
+from beekeepy._apis.abc.api import HandleT
+from beekeepy._apis.abc.sendable import AsyncSendable, SyncSendable
 
 
 class AbstractApiCollection(Generic[HandleT]):
@@ -12,15 +13,15 @@ class AbstractApiCollection(Generic[HandleT]):
         self._owner = owner
 
 
-class AbstractAsyncApiCollection(AbstractApiCollection[AsyncHandleT]):
+class AbstractAsyncApiCollection(AbstractApiCollection[AsyncSendable]):
     """Base class for Async Api Collections."""
 
-    def __init__(self, owner: AsyncHandleT) -> None:
+    def __init__(self, owner: AsyncSendable) -> None:
         super().__init__(owner)
 
 
-class AbstractSyncApiCollection(AbstractApiCollection[SyncHandleT]):
+class AbstractSyncApiCollection(AbstractApiCollection[SyncSendable]):
     """Base class for Sync Api Collections."""
 
-    def __init__(self, owner: SyncHandleT) -> None:
+    def __init__(self, owner: SyncSendable) -> None:
         super().__init__(owner)
diff --git a/beekeepy/beekeepy/_apis/abc/sendable.py b/beekeepy/beekeepy/_apis/abc/sendable.py
new file mode 100644
index 00000000..3f2e5ac1
--- /dev/null
+++ b/beekeepy/beekeepy/_apis/abc/sendable.py
@@ -0,0 +1,21 @@
+from __future__ import annotations
+
+from abc import ABC, abstractmethod
+from typing import TYPE_CHECKING
+
+if TYPE_CHECKING:
+    from schemas.jsonrpc import ExpectResultT, JSONRPCResult
+
+
+class SyncSendable(ABC):
+    @abstractmethod
+    def _send(
+        self, *, endpoint: str, params: str, expected_type: type[ExpectResultT]
+    ) -> JSONRPCResult[ExpectResultT]: ...
+
+
+class AsyncSendable(ABC):
+    @abstractmethod
+    async def _async_send(
+        self, *, endpoint: str, params: str, expected_type: type[ExpectResultT]
+    ) -> JSONRPCResult[ExpectResultT]: ...
diff --git a/beekeepy/beekeepy/_remote_handle/api/session_holder.py b/beekeepy/beekeepy/_apis/abc/session_holder.py
similarity index 88%
rename from beekeepy/beekeepy/_remote_handle/api/session_holder.py
rename to beekeepy/beekeepy/_apis/abc/session_holder.py
index 2c5b3d12..9e314b8b 100644
--- a/beekeepy/beekeepy/_remote_handle/api/session_holder.py
+++ b/beekeepy/beekeepy/_apis/abc/session_holder.py
@@ -1,8 +1,9 @@
 from __future__ import annotations
 
-from abc import abstractmethod
+from abc import ABC, abstractmethod
 from typing import Any
 
+from beekeepy._apis.abc.sendable import AsyncSendable, SyncSendable
 from schemas.apis.beekeeper_api import CreateSession
 
 __all__ = ["SyncSessionHolder", "AsyncSessionHolder"]
@@ -37,7 +38,7 @@ class SessionHolder:
         return self.__session
 
 
-class SyncSessionHolder(SessionHolder):
+class SyncSessionHolder(SyncSendable, SessionHolder, ABC):
     @abstractmethod
     def _acquire_session_token(self) -> str: ...
 
@@ -48,7 +49,7 @@ class SyncSessionHolder(SessionHolder):
         return self._check_and_return_session()
 
 
-class AsyncSessionHolder(SessionHolder):
+class AsyncSessionHolder(AsyncSendable, SessionHolder, ABC):
     @abstractmethod
     async def _acquire_session_token(self) -> str: ...
 
diff --git a/beekeepy/beekeepy/_apis/app_status_api/__init__.py b/beekeepy/beekeepy/_apis/app_status_api/__init__.py
new file mode 100644
index 00000000..58d93619
--- /dev/null
+++ b/beekeepy/beekeepy/_apis/app_status_api/__init__.py
@@ -0,0 +1,19 @@
+from __future__ import annotations
+
+from beekeepy._apis.app_status_api.api_collection import (
+    AppStatusProbeAsyncApiCollection,
+    AppStatusProbeSyncApiCollection,
+)
+from beekeepy._apis.app_status_api.async_api import (
+    AppStatusApi as AsyncAppStatusApi,
+)
+from beekeepy._apis.app_status_api.sync_api import (
+    AppStatusApi as SyncAppStatusApi,
+)
+
+__all__ = [
+    "AsyncAppStatusApi",
+    "SyncAppStatusApi",
+    "AppStatusProbeAsyncApiCollection",
+    "AppStatusProbeSyncApiCollection",
+]
diff --git a/beekeepy/beekeepy/_apis/app_status_api/api_collection.py b/beekeepy/beekeepy/_apis/app_status_api/api_collection.py
new file mode 100644
index 00000000..a61fc45d
--- /dev/null
+++ b/beekeepy/beekeepy/_apis/app_status_api/api_collection.py
@@ -0,0 +1,32 @@
+from __future__ import annotations
+
+from typing import TYPE_CHECKING
+
+from beekeepy._apis.abc.api_collection import AbstractAsyncApiCollection, AbstractSyncApiCollection
+from beekeepy._apis.app_status_api.async_api import AppStatusApi as AsyncAppStatusApi
+from beekeepy._apis.app_status_api.sync_api import AppStatusApi as SyncAppStatusApi
+
+if TYPE_CHECKING:
+    from beekeepy._apis.abc.sendable import AsyncSendable, SyncSendable
+
+
+class AppStatusProbeSyncApiCollection(AbstractSyncApiCollection):
+    """Beekeepers collection of available apis in async version."""
+
+    _owner: SyncSendable
+
+    def __init__(self, owner: SyncSendable) -> None:
+        super().__init__(owner)
+        self.app_status = SyncAppStatusApi(owner=self._owner)
+        self.app_status_api = self.app_status
+
+
+class AppStatusProbeAsyncApiCollection(AbstractAsyncApiCollection):
+    """Beekeepers collection of available apis in async version."""
+
+    _owner: AsyncSendable
+
+    def __init__(self, owner: AsyncSendable) -> None:
+        super().__init__(owner)
+        self.app_status = AsyncAppStatusApi(owner=self._owner)
+        self.app_status_api = self.app_status
diff --git a/beekeepy/beekeepy/_apis/app_status_api/async_api.py b/beekeepy/beekeepy/_apis/app_status_api/async_api.py
new file mode 100644
index 00000000..b6c36b7f
--- /dev/null
+++ b/beekeepy/beekeepy/_apis/app_status_api/async_api.py
@@ -0,0 +1,12 @@
+from __future__ import annotations
+
+from beekeepy._apis.abc.api import AbstractAsyncApi
+from schemas.apis import app_status_api  # noqa: TCH001
+
+
+class AppStatusApi(AbstractAsyncApi):
+    api = AbstractAsyncApi._endpoint
+
+    @api
+    async def get_app_status(self) -> app_status_api.GetAppStatus:
+        raise NotImplementedError
diff --git a/beekeepy/beekeepy/_apis/app_status_api/sync_api.py b/beekeepy/beekeepy/_apis/app_status_api/sync_api.py
new file mode 100644
index 00000000..50633a60
--- /dev/null
+++ b/beekeepy/beekeepy/_apis/app_status_api/sync_api.py
@@ -0,0 +1,12 @@
+from __future__ import annotations
+
+from beekeepy._apis.abc.api import AbstractSyncApi
+from schemas.apis import app_status_api  # noqa: TCH001
+
+
+class AppStatusApi(AbstractSyncApi):
+    api = AbstractSyncApi._endpoint
+
+    @api
+    def get_app_status(self) -> app_status_api.GetAppStatus:
+        raise NotImplementedError
diff --git a/beekeepy/beekeepy/_remote_handle/api/apply_session_token.py b/beekeepy/beekeepy/_apis/apply_session_token.py
similarity index 76%
rename from beekeepy/beekeepy/_remote_handle/api/apply_session_token.py
rename to beekeepy/beekeepy/_apis/apply_session_token.py
index e7ed3274..a440a918 100644
--- a/beekeepy/beekeepy/_remote_handle/api/apply_session_token.py
+++ b/beekeepy/beekeepy/_apis/apply_session_token.py
@@ -3,8 +3,7 @@ from __future__ import annotations
 from typing import TYPE_CHECKING
 
 if TYPE_CHECKING:
-    from beekeepy._remote_handle.abc.api import ApiArgumentsToSerialize
-    from beekeepy._remote_handle.api.session_holder import AsyncSessionHolder, SyncSessionHolder
+    from beekeepy._apis.abc import ApiArgumentsToSerialize, AsyncSessionHolder, SyncSessionHolder
 
 
 def sync_apply_session_token(owner: SyncSessionHolder, arguments: ApiArgumentsToSerialize) -> ApiArgumentsToSerialize:
diff --git a/beekeepy/beekeepy/_apis/beekeeper_api/__init__.py b/beekeepy/beekeepy/_apis/beekeeper_api/__init__.py
new file mode 100644
index 00000000..dca7d2cf
--- /dev/null
+++ b/beekeepy/beekeepy/_apis/beekeeper_api/__init__.py
@@ -0,0 +1,14 @@
+from __future__ import annotations
+
+from beekeepy._apis.beekeeper_api.api_collection import (
+    BeekeeperAsyncApiCollection,
+    BeekeeperSyncApiCollection,
+)
+from beekeepy._apis.beekeeper_api.async_api import (
+    BeekeeperApi as AsyncBeekeeperApi,
+)
+from beekeepy._apis.beekeeper_api.sync_api import (
+    BeekeeperApi as SyncBeekeeperApi,
+)
+
+__all__ = ["AsyncBeekeeperApi", "SyncBeekeeperApi", "BeekeeperAsyncApiCollection", "BeekeeperSyncApiCollection"]
diff --git a/beekeepy/beekeepy/_apis/beekeeper_api/api_collection.py b/beekeepy/beekeepy/_apis/beekeeper_api/api_collection.py
new file mode 100644
index 00000000..26744fc9
--- /dev/null
+++ b/beekeepy/beekeepy/_apis/beekeeper_api/api_collection.py
@@ -0,0 +1,32 @@
+from __future__ import annotations
+
+from typing import TYPE_CHECKING
+
+from beekeepy._apis.app_status_api import AppStatusProbeAsyncApiCollection, AppStatusProbeSyncApiCollection
+from beekeepy._apis.beekeeper_api.async_api import BeekeeperApi as AsyncBeekeeperApi
+from beekeepy._apis.beekeeper_api.sync_api import BeekeeperApi as SyncBeekeeperApi
+
+if TYPE_CHECKING:
+    from beekeepy._apis.abc import AsyncSessionHolder, SyncSessionHolder
+
+
+class BeekeeperAsyncApiCollection(AppStatusProbeAsyncApiCollection):
+    """Beekeepers collection of available apis in async version."""
+
+    _owner: AsyncSessionHolder
+
+    def __init__(self, owner: AsyncSessionHolder) -> None:
+        super().__init__(owner)
+        self.beekeeper = AsyncBeekeeperApi(owner=self._owner)
+        self.beekeeper_api = self.beekeeper
+
+
+class BeekeeperSyncApiCollection(AppStatusProbeSyncApiCollection):
+    """Beekeepers collection of available apis in async version."""
+
+    _owner: SyncSessionHolder
+
+    def __init__(self, owner: SyncSessionHolder) -> None:
+        super().__init__(owner)
+        self.beekeeper = SyncBeekeeperApi(owner=self._owner)
+        self.beekeeper_api = self.beekeeper
diff --git a/beekeepy/beekeepy/_remote_handle/api/async_api.py b/beekeepy/beekeepy/_apis/beekeeper_api/async_api.py
similarity index 90%
rename from beekeepy/beekeepy/_remote_handle/api/async_api.py
rename to beekeepy/beekeepy/_apis/beekeeper_api/async_api.py
index 336e20c1..ba109ee1 100644
--- a/beekeepy/beekeepy/_remote_handle/api/async_api.py
+++ b/beekeepy/beekeepy/_apis/beekeeper_api/async_api.py
@@ -1,24 +1,18 @@
 from __future__ import annotations
 
-from typing import TYPE_CHECKING
-
-from beekeepy._remote_handle.abc.api import AbstractAsyncApi, ApiArgumentsToSerialize, AsyncHandleT
-from beekeepy._remote_handle.api.apply_session_token import async_apply_session_token
-from beekeepy._remote_handle.api.beekeeper_api_commons import BeekeeperApiCommons
-from beekeepy._remote_handle.api.session_holder import AsyncSessionHolder
+from beekeepy._apis.abc import AbstractAsyncApi, ApiArgumentsToSerialize, AsyncSendable, AsyncSessionHolder
+from beekeepy._apis.apply_session_token import async_apply_session_token
+from beekeepy._apis.beekeeper_api.beekeeper_api_commons import BeekeeperApiCommons
 from schemas.apis import beekeeper_api  # noqa: TCH001
 
-if TYPE_CHECKING:
-    from beekeepy._remote_handle.beekeeper import AsyncBeekeeper, _AsyncSessionBatchHandle
-
 
-class BeekeeperApi(AbstractAsyncApi, BeekeeperApiCommons[AsyncHandleT]):
+class BeekeeperApi(AbstractAsyncApi, BeekeeperApiCommons[AsyncSendable]):
     """Set of endpoints, that allows asynchronous communication with beekeeper service."""
 
     api = AbstractAsyncApi._endpoint
-    _owner: AsyncBeekeeper | _AsyncSessionBatchHandle
+    _owner: AsyncSessionHolder
 
-    def __init__(self, owner: AsyncBeekeeper | _AsyncSessionBatchHandle) -> None:
+    def __init__(self, owner: AsyncSessionHolder) -> None:
         self._verify_is_owner_can_hold_session_token(owner=owner)
         super().__init__(owner=owner)
 
@@ -216,14 +210,13 @@ class BeekeeperApi(AbstractAsyncApi, BeekeeperApiCommons[AsyncHandleT]):
         raise NotImplementedError
 
     @api
-    async def create_session(self, *, notifications_endpoint: str = "", salt: str = "") -> beekeeper_api.CreateSession:
+    async def create_session(self, *, salt: str = "") -> beekeeper_api.CreateSession:
         """Creates session.
 
         Note:
             This is called automatically when connection with beekeeper is establish, no need to call it explicitly.
 
         Args:
-            notifications_endpoint: endpoint on which notifications of status will be broadcasted. (defaults: "")
             salt: used for generation of session token
 
         Returns:
diff --git a/beekeepy/beekeepy/_remote_handle/api/beekeeper_api_commons.py b/beekeepy/beekeepy/_apis/beekeeper_api/beekeeper_api_commons.py
similarity index 86%
rename from beekeepy/beekeepy/_remote_handle/api/beekeeper_api_commons.py
rename to beekeepy/beekeepy/_apis/beekeeper_api/beekeeper_api_commons.py
index 16b55fb4..718cc059 100644
--- a/beekeepy/beekeepy/_remote_handle/api/beekeeper_api_commons.py
+++ b/beekeepy/beekeepy/_apis/beekeeper_api/beekeeper_api_commons.py
@@ -3,10 +3,10 @@ from __future__ import annotations
 from abc import abstractmethod
 from typing import TYPE_CHECKING, Any, Generic, Protocol
 
-from beekeepy._remote_handle.abc.api import HandleT
+from beekeepy._apis.abc.api import HandleT
 
 if TYPE_CHECKING:
-    from beekeepy._remote_handle.api.session_holder import AsyncSessionHolder, SyncSessionHolder
+    from beekeepy._apis.abc import AsyncSessionHolder, SyncSessionHolder
 
 
 class CreateSessionActionProtocol(Protocol):
diff --git a/beekeepy/beekeepy/_remote_handle/api/sync_api.py b/beekeepy/beekeepy/_apis/beekeeper_api/sync_api.py
similarity index 81%
rename from beekeepy/beekeepy/_remote_handle/api/sync_api.py
rename to beekeepy/beekeepy/_apis/beekeeper_api/sync_api.py
index 1360bf52..9b236f63 100644
--- a/beekeepy/beekeepy/_remote_handle/api/sync_api.py
+++ b/beekeepy/beekeepy/_apis/beekeeper_api/sync_api.py
@@ -1,23 +1,17 @@
 from __future__ import annotations
 
-from typing import TYPE_CHECKING, cast
-
-from beekeepy._remote_handle.abc.api import AbstractSyncApi, ApiArgumentsToSerialize, SyncHandleT
-from beekeepy._remote_handle.api.apply_session_token import sync_apply_session_token
-from beekeepy._remote_handle.api.beekeeper_api_commons import BeekeeperApiCommons
-from beekeepy._remote_handle.api.session_holder import SyncSessionHolder
+from beekeepy._apis.abc import AbstractSyncApi, ApiArgumentsToSerialize, SyncSendable, SyncSessionHolder
+from beekeepy._apis.apply_session_token import sync_apply_session_token
+from beekeepy._apis.beekeeper_api.beekeeper_api_commons import BeekeeperApiCommons
 from schemas.apis import beekeeper_api  # noqa: TCH001
 
-if TYPE_CHECKING:
-    from beekeepy._remote_handle.beekeeper import Beekeeper, _SyncSessionBatchHandle
-
 
-class BeekeeperApi(AbstractSyncApi, BeekeeperApiCommons[SyncHandleT]):
+class BeekeeperApi(AbstractSyncApi, BeekeeperApiCommons[SyncSendable]):
     api = AbstractSyncApi._endpoint
 
-    _owner: Beekeeper | _SyncSessionBatchHandle
+    _owner: SyncSessionHolder
 
-    def __init__(self, owner: Beekeeper | _SyncSessionBatchHandle) -> None:
+    def __init__(self, owner: SyncSessionHolder) -> None:
         self._verify_is_owner_can_hold_session_token(owner=owner)
         super().__init__(owner=owner)
 
@@ -26,7 +20,7 @@ class BeekeeperApi(AbstractSyncApi, BeekeeperApiCommons[SyncHandleT]):
     ) -> ApiArgumentsToSerialize:
         if not self._token_required(endpoint_name):
             return super()._additional_arguments_actions(endpoint_name, arguments)
-        return sync_apply_session_token(cast(SyncSessionHolder, self._owner), arguments)
+        return sync_apply_session_token(self._owner, arguments)
 
     def _get_requires_session_holder_type(self) -> type[SyncSessionHolder]:
         return SyncSessionHolder
@@ -94,7 +88,7 @@ class BeekeeperApi(AbstractSyncApi, BeekeeperApiCommons[SyncHandleT]):
         raise NotImplementedError
 
     @api
-    def create_session(self, *, notifications_endpoint: str = "", salt: str = "") -> beekeeper_api.CreateSession:
+    def create_session(self, *, salt: str = "") -> beekeeper_api.CreateSession:
         raise NotImplementedError
 
     @api
diff --git a/beekeepy/beekeepy/_communication/__init__.py b/beekeepy/beekeepy/_communication/__init__.py
index e69de29b..f56725c6 100644
--- a/beekeepy/beekeepy/_communication/__init__.py
+++ b/beekeepy/beekeepy/_communication/__init__.py
@@ -0,0 +1,28 @@
+from __future__ import annotations
+
+from beekeepy._communication import rules
+from beekeepy._communication.abc.communicator import AbstractCommunicator
+from beekeepy._communication.abc.overseer import AbstractOverseer
+from beekeepy._communication.aiohttp_communicator import AioHttpCommunicator
+from beekeepy._communication.httpx_communicator import HttpxCommunicator
+from beekeepy._communication.overseers import CommonOverseer, StrictOverseer
+from beekeepy._communication.request_communicator import RequestCommunicator
+from beekeepy._communication.settings import CommunicationSettings
+from beekeepy._communication.url import AnyUrl, HttpUrl, P2PUrl, Url, WsUrl
+
+__all__ = [
+    "CommunicationSettings",
+    "CommonOverseer",
+    "StrictOverseer",
+    "AioHttpCommunicator",
+    "HttpxCommunicator",
+    "RequestCommunicator",
+    "AnyUrl",
+    "HttpUrl",
+    "P2PUrl",
+    "Url",
+    "WsUrl",
+    "AbstractCommunicator",
+    "AbstractOverseer",
+    "rules",
+]
diff --git a/beekeepy/beekeepy/_communication/abc/communicator.py b/beekeepy/beekeepy/_communication/abc/communicator.py
index 2dae1791..d72446d9 100644
--- a/beekeepy/beekeepy/_communication/abc/communicator.py
+++ b/beekeepy/beekeepy/_communication/abc/communicator.py
@@ -7,13 +7,13 @@ from threading import Thread
 from typing import TYPE_CHECKING, Any, Awaitable
 
 from beekeepy._communication.settings import CommunicationSettings
-from beekeepy._interface.settings_holder import SharedSettingsHolder
-from beekeepy._interface.stopwatch import Stopwatch
+from beekeepy._utilities.settings_holder import SharedSettingsHolder
+from beekeepy._utilities.stopwatch import Stopwatch
 from beekeepy.exceptions import CommunicationError, TimeoutExceededError, UnknownDecisionPathError
 
 if TYPE_CHECKING:
-    from beekeepy._interface.stopwatch import StopwatchResult
-    from beekeepy._interface.url import HttpUrl
+    from beekeepy._communication.url import HttpUrl
+    from beekeepy._utilities.stopwatch import StopwatchResult
 
 
 class AbstractCommunicator(SharedSettingsHolder[CommunicationSettings], ABC):
diff --git a/beekeepy/beekeepy/_communication/abc/http_server_observer.py b/beekeepy/beekeepy/_communication/abc/http_server_observer.py
deleted file mode 100644
index 6d4efcee..00000000
--- a/beekeepy/beekeepy/_communication/abc/http_server_observer.py
+++ /dev/null
@@ -1,17 +0,0 @@
-from __future__ import annotations
-
-from abc import ABC, abstractmethod
-from typing import Any
-
-
-class HttpServerObserver(ABC):
-    @abstractmethod
-    async def data_received(self, data: dict[str, Any]) -> None:
-        """Called when any data is received via PUT method.
-
-        Args:
-            data: data received as body
-
-        Returns:
-            Nothing.
-        """
diff --git a/beekeepy/beekeepy/_communication/abc/notification_handler.py b/beekeepy/beekeepy/_communication/abc/notification_handler.py
deleted file mode 100644
index da69e8b5..00000000
--- a/beekeepy/beekeepy/_communication/abc/notification_handler.py
+++ /dev/null
@@ -1,21 +0,0 @@
-from __future__ import annotations
-
-from abc import ABC, abstractmethod
-from typing import Any
-
-from beekeepy._communication.abc.http_server_observer import HttpServerObserver
-from schemas.notifications import KnownNotificationT, Notification
-
-
-class NotificationHandler(HttpServerObserver, ABC):
-    async def data_received(self, data: dict[str, Any]) -> None:
-        deserialized_notification = Notification(**data)
-        await self.handle_notification(deserialized_notification)
-
-    @abstractmethod
-    async def handle_notification(self, notification: Notification[KnownNotificationT]) -> None:
-        """Method called after properly serializing notification.
-
-        Args:
-            notification: received notification object
-        """
diff --git a/beekeepy/beekeepy/_communication/abc/overseer.py b/beekeepy/beekeepy/_communication/abc/overseer.py
index 30d75296..756eae86 100644
--- a/beekeepy/beekeepy/_communication/abc/overseer.py
+++ b/beekeepy/beekeepy/_communication/abc/overseer.py
@@ -5,19 +5,16 @@ from abc import ABC, abstractmethod
 from enum import IntEnum
 from typing import TYPE_CHECKING, Any, Callable, ClassVar, Sequence
 
-from beekeepy._interface.context import SelfContextSync
-from beekeepy.exceptions import (
-    GroupedErrorsError,
-    UnknownDecisionPathError,
-)
+from beekeepy._utilities.context import SelfContextSync
+from beekeepy.exceptions import GroupedErrorsError, Json, UnknownDecisionPathError
 
 if TYPE_CHECKING:
     from types import TracebackType
 
     from beekeepy._communication.abc.communicator import AbstractCommunicator
     from beekeepy._communication.abc.rules import Rules, RulesClassifier
-    from beekeepy._interface.url import HttpUrl
-    from beekeepy.exceptions import Json, OverseerError
+    from beekeepy._communication.url import HttpUrl
+    from beekeepy.exceptions import OverseerError
 
 
 __all__ = ["AbstractOverseer"]
diff --git a/beekeepy/beekeepy/_communication/abc/rules.py b/beekeepy/beekeepy/_communication/abc/rules.py
index 335f83ae..3e6114d2 100644
--- a/beekeepy/beekeepy/_communication/abc/rules.py
+++ b/beekeepy/beekeepy/_communication/abc/rules.py
@@ -5,7 +5,7 @@ from dataclasses import dataclass
 from typing import TYPE_CHECKING, Sequence
 
 if TYPE_CHECKING:
-    from beekeepy._interface.url import HttpUrl
+    from beekeepy._communication.url import HttpUrl
     from beekeepy.exceptions import Json, OverseerError
 
 
diff --git a/beekeepy/beekeepy/_communication/aiohttp_communicator.py b/beekeepy/beekeepy/_communication/aiohttp_communicator.py
index f20e9442..07095537 100644
--- a/beekeepy/beekeepy/_communication/aiohttp_communicator.py
+++ b/beekeepy/beekeepy/_communication/aiohttp_communicator.py
@@ -13,8 +13,8 @@ from beekeepy.exceptions import CommunicationError, UnknownDecisionPathError
 
 if TYPE_CHECKING:
     from beekeepy._communication.settings import CommunicationSettings
-    from beekeepy._interface.stopwatch import StopwatchResult
-    from beekeepy._interface.url import HttpUrl
+    from beekeepy._communication.url import HttpUrl
+    from beekeepy._utilities.stopwatch import StopwatchResult
 
 
 class AioHttpCommunicator(AbstractCommunicator):
diff --git a/beekeepy/beekeepy/_communication/appbase_notification_handler.py b/beekeepy/beekeepy/_communication/appbase_notification_handler.py
deleted file mode 100644
index c9d0241a..00000000
--- a/beekeepy/beekeepy/_communication/appbase_notification_handler.py
+++ /dev/null
@@ -1,40 +0,0 @@
-from __future__ import annotations
-
-from typing import TYPE_CHECKING
-
-from beekeepy._communication.notification_decorator import notification
-from beekeepy._communication.universal_notification_server import UniversalNotificationHandler
-from schemas.notifications import Error, Status, WebserverListening
-
-if TYPE_CHECKING:
-    from schemas.notifications import Notification
-
-
-class AppbaseNotificationHandler(UniversalNotificationHandler):
-    @notification(WebserverListening, condition=lambda n: n.value.type_ == "HTTP")
-    async def _on_http_webserver_bind(self, notification: Notification[WebserverListening]) -> None:
-        await self.on_http_webserver_bind(notification)
-
-    @notification(WebserverListening, condition=lambda n: n.value.type_ == "WS")
-    async def _on_ws_webserver_bind(self, notification: Notification[WebserverListening]) -> None:
-        await self.on_ws_webserver_bind(notification)
-
-    @notification(Status)
-    async def _on_status_changed(self, notification: Notification[Status]) -> None:
-        await self.on_status_changed(notification)
-
-    @notification(Error)
-    async def _on_error(self, notification: Notification[Error]) -> None:
-        await self.on_error(notification)
-
-    async def on_http_webserver_bind(self, notification: Notification[WebserverListening]) -> None:
-        """Called when hived reports http server to be ready."""
-
-    async def on_ws_webserver_bind(self, notification: Notification[WebserverListening]) -> None:
-        """Called when hived reports ws server to be ready."""
-
-    async def on_status_changed(self, notification: Notification[Status]) -> None:
-        """Called when status of notifier changed."""
-
-    async def on_error(self, notification: Notification[Error]) -> None:
-        """Called when notifier reports an error."""
diff --git a/beekeepy/beekeepy/_communication/async_server.py b/beekeepy/beekeepy/_communication/async_server.py
deleted file mode 100644
index 5e6de354..00000000
--- a/beekeepy/beekeepy/_communication/async_server.py
+++ /dev/null
@@ -1,104 +0,0 @@
-from __future__ import annotations
-
-import asyncio
-from http import HTTPStatus
-from typing import TYPE_CHECKING
-
-from aiohttp import web
-from typing_extensions import Self
-
-from beekeepy._interface.context import ContextAsync
-from beekeepy._interface.url import HttpUrl
-from beekeepy.exceptions import BeekeepyError
-
-if TYPE_CHECKING:
-    from socket import socket
-
-    from beekeepy._communication.abc.http_server_observer import HttpServerObserver
-
-
-class AsyncHttpServerError(BeekeepyError):
-    pass
-
-
-class ServerNotRunningError(AsyncHttpServerError):
-    def __init__(self) -> None:
-        super().__init__("Server is not running. Call run() first.")
-
-
-class ServerAlreadyRunningError(AsyncHttpServerError):
-    def __init__(self) -> None:
-        super().__init__("Server is already running. Call close() first.")
-
-
-class ServerSetupError(AsyncHttpServerError):
-    def __init__(self, message: str) -> None:
-        super().__init__(message)
-
-
-class AsyncHttpServer(ContextAsync[Self]):  # type: ignore[misc]
-    __ADDRESS = HttpUrl("0.0.0.0:0")
-
-    def __init__(self, observer: HttpServerObserver, notification_endpoint: HttpUrl | None) -> None:
-        self.__observer = observer
-        self._app = web.Application()
-        self.__site: web.TCPSite | None = None
-        self.__running: bool = False
-        self.__notification_endpoint = notification_endpoint
-        self._setup_routes()
-
-    def _setup_routes(self) -> None:
-        async def handle_put_method(request: web.Request) -> web.Response:
-            await self.__observer.data_received(await request.json())
-            return web.Response(status=HTTPStatus.NO_CONTENT)
-
-        self._app.router.add_route("PUT", "/", handle_put_method)
-
-    @property
-    def port(self) -> int:
-        if not self.__site:
-            raise ServerNotRunningError
-        server: asyncio.base_events.Server | None = self.__site._server  # type: ignore[assignment]
-        if server is None:
-            raise ServerSetupError("self.__site.server is None")
-
-        server_socket: socket = server.sockets[0]
-        address_tuple: tuple[str, int] = server_socket.getsockname()
-
-        if not (
-            isinstance(address_tuple, tuple) and isinstance(address_tuple[0], str) and isinstance(address_tuple[1], int)
-        ):
-            raise ServerSetupError(f"address_tuple has not recognizable types: {address_tuple}")
-
-        return address_tuple[1]
-
-    async def run(self) -> None:
-        if self.__site:
-            raise ServerAlreadyRunningError
-
-        time_between_checks_is_server_running = 0.5
-
-        runner = web.AppRunner(self._app, access_log=False)
-        await runner.setup()
-        address = self.__notification_endpoint or self.__ADDRESS
-        self.__site = web.TCPSite(runner, address.address, address.port)
-        await self.__site.start()
-        self.__running = True
-        try:
-            while self.__running:
-                await asyncio.sleep(time_between_checks_is_server_running)
-        finally:
-            await self.__site.stop()
-            self.__site = None
-
-    def close(self) -> None:
-        if not self.__site:
-            raise ServerNotRunningError
-        self.__running = False
-
-    async def _aenter(self) -> Self:
-        await self.run()
-        return self
-
-    async def _afinally(self) -> None:
-        self.close()
diff --git a/beekeepy/beekeepy/_communication/httpx_communicator.py b/beekeepy/beekeepy/_communication/httpx_communicator.py
index 30aaf85b..2162cc4b 100644
--- a/beekeepy/beekeepy/_communication/httpx_communicator.py
+++ b/beekeepy/beekeepy/_communication/httpx_communicator.py
@@ -11,8 +11,8 @@ from beekeepy.exceptions import CommunicationError
 
 if TYPE_CHECKING:
     from beekeepy._communication.settings import CommunicationSettings
-    from beekeepy._interface.stopwatch import StopwatchResult
-    from beekeepy._interface.url import HttpUrl
+    from beekeepy._communication.url import HttpUrl
+    from beekeepy._utilities.stopwatch import StopwatchResult
 
 ClientTypes = httpx.AsyncClient | httpx.Client
 
diff --git a/beekeepy/beekeepy/_communication/notification_decorator.py b/beekeepy/beekeepy/_communication/notification_decorator.py
deleted file mode 100644
index caa22a44..00000000
--- a/beekeepy/beekeepy/_communication/notification_decorator.py
+++ /dev/null
@@ -1,43 +0,0 @@
-from __future__ import annotations
-
-from collections.abc import Awaitable, Callable  # noqa: TCH003
-from typing import Any, Generic
-
-from pydantic.generics import GenericModel
-
-from schemas.notifications import KnownNotificationT, Notification
-
-
-class _NotificationHandlerWrapper(GenericModel, Generic[KnownNotificationT]):
-    notification_name: str
-    notification_handler: Callable[[Any, Notification[KnownNotificationT]], Awaitable[None]]
-    notification_condition: Callable[[Notification[KnownNotificationT]], bool]
-
-    async def call(self, this: Any, notification: Notification[KnownNotificationT]) -> None:
-        await self.notification_handler(this, notification)
-
-    async def __call__(self, this: Any, notification: Notification[KnownNotificationT]) -> Any:
-        return self.call(this, notification)
-
-
-def notification(
-    type_: type[KnownNotificationT],
-    /,
-    *,
-    condition: Callable[[Notification[KnownNotificationT]], bool] | None = None,
-) -> Callable[
-    [Callable[[Any, Notification[KnownNotificationT]], Awaitable[None]]],
-    _NotificationHandlerWrapper[KnownNotificationT],
-]:
-    def wrapper(
-        callback: Callable[[Any, Notification[KnownNotificationT]], Awaitable[None]],
-    ) -> _NotificationHandlerWrapper[KnownNotificationT]:
-        result_cls = _NotificationHandlerWrapper[type_]  # type: ignore[valid-type]
-        result_cls.update_forward_refs(**locals())
-        return result_cls(  # type: ignore[return-value]
-            notification_name=type_.get_notification_name(),
-            notification_handler=callback,
-            notification_condition=condition or (lambda _: True),
-        )
-
-    return wrapper
diff --git a/beekeepy/beekeepy/_communication/request_communicator.py b/beekeepy/beekeepy/_communication/request_communicator.py
index 258b494b..71b73d06 100644
--- a/beekeepy/beekeepy/_communication/request_communicator.py
+++ b/beekeepy/beekeepy/_communication/request_communicator.py
@@ -11,8 +11,8 @@ from beekeepy.exceptions import CommunicationError
 
 if TYPE_CHECKING:
     from beekeepy._communication.settings import CommunicationSettings
-    from beekeepy._interface.stopwatch import StopwatchResult
-    from beekeepy._interface.url import HttpUrl
+    from beekeepy._communication.url import HttpUrl
+    from beekeepy._utilities.stopwatch import StopwatchResult
 
 
 class RequestCommunicator(AbstractCommunicator):
diff --git a/beekeepy/beekeepy/_communication/settings.py b/beekeepy/beekeepy/_communication/settings.py
index ee6feaef..3a0d3a7c 100644
--- a/beekeepy/beekeepy/_communication/settings.py
+++ b/beekeepy/beekeepy/_communication/settings.py
@@ -6,7 +6,7 @@ from typing import TYPE_CHECKING, Any, ClassVar, cast
 
 from pydantic import BaseModel, Field
 
-from beekeepy._interface.url import Url
+from beekeepy._communication.url import Url
 
 if TYPE_CHECKING:
     from collections.abc import Callable
diff --git a/beekeepy/beekeepy/_communication/universal_notification_server.py b/beekeepy/beekeepy/_communication/universal_notification_server.py
deleted file mode 100644
index 7813e49b..00000000
--- a/beekeepy/beekeepy/_communication/universal_notification_server.py
+++ /dev/null
@@ -1,87 +0,0 @@
-from __future__ import annotations
-
-import asyncio
-from collections import defaultdict
-from threading import Thread
-from time import sleep
-from typing import TYPE_CHECKING, Any, Final
-
-from beekeepy._communication.abc.notification_handler import NotificationHandler
-from beekeepy._communication.async_server import AsyncHttpServer
-from beekeepy._communication.notification_decorator import _NotificationHandlerWrapper
-from beekeepy._interface.context import ContextSync
-from beekeepy.exceptions import BeekeepyError
-
-if TYPE_CHECKING:
-    from beekeepy._interface.url import HttpUrl
-    from schemas.notifications import KnownNotificationT, Notification
-
-
-class UnhandledNotificationError(BeekeepyError):
-    def __init__(self, notification: Notification[KnownNotificationT]) -> None:
-        super().__init__(
-            f"Notification `{notification.name}` does not have any registered method to be passed to.", notification
-        )
-
-
-class UniversalNotificationHandler(NotificationHandler):
-    def __init__(self, *args: Any, **kwargs: Any) -> None:
-        self.__registered_notifications: defaultdict[str, list[_NotificationHandlerWrapper[Any]]] = defaultdict(list)
-        super().__init__(*args, **kwargs)
-
-    def setup(self) -> None:
-        for member_name in dir(self):
-            member_value = getattr(self, member_name)
-            if isinstance(member_value, _NotificationHandlerWrapper):
-                self.__registered_notifications[member_value.notification_name].append(member_value)
-
-    async def handle_notification(self, notification: Notification[KnownNotificationT]) -> None:
-        if (callbacks := self.__registered_notifications.get(notification.name)) is not None:
-            for callback in callbacks:
-                if callback.notification_condition(notification):
-                    await callback.call(self, notification)
-                    return
-
-        raise UnhandledNotificationError(notification)
-
-
-class UniversalNotificationServer(ContextSync[int]):
-    def __init__(
-        self,
-        event_handler: UniversalNotificationHandler,
-        notification_endpoint: HttpUrl | None = None,
-        *,
-        thread_name: str | None = None,
-    ) -> None:
-        self.__event_handler = event_handler
-        self.__event_handler.setup()
-        self.__http_server = AsyncHttpServer(self.__event_handler, notification_endpoint=notification_endpoint)
-        self.__server_thread: Thread | None = None
-        self.__thread_name = thread_name
-
-    def run(self) -> int:
-        time_to_launch_notification_server: Final[float] = 0.5
-        assert self.__server_thread is None, "Server thread is not None; Is server still running?"
-
-        self.__server_thread = Thread(target=asyncio.run, args=(self.__http_server.run(),), name=self.__thread_name)
-        self.__server_thread.start()
-        sleep(time_to_launch_notification_server)
-        return self.__http_server.port
-
-    def close(self) -> None:
-        if self.__server_thread is None:
-            return
-
-        self.__http_server.close()
-        self.__server_thread.join()
-        self.__server_thread = None
-
-    @property
-    def port(self) -> int:
-        return self.__http_server.port
-
-    def _enter(self) -> int:
-        return self.run()
-
-    def _finally(self) -> None:
-        self.close()
diff --git a/beekeepy/beekeepy/_interface/url.py b/beekeepy/beekeepy/_communication/url.py
similarity index 88%
rename from beekeepy/beekeepy/_interface/url.py
rename to beekeepy/beekeepy/_communication/url.py
index f7a8937f..c7ce7d39 100644
--- a/beekeepy/beekeepy/_interface/url.py
+++ b/beekeepy/beekeepy/_communication/url.py
@@ -3,6 +3,8 @@ from __future__ import annotations
 from typing import Generic, Literal, TypeVar, get_args
 from urllib.parse import urlparse
 
+from typing_extensions import Self
+
 P2PProtocolT = Literal[""]
 HttpProtocolT = Literal["http", "https"]
 WsProtocolT = Literal["ws", "wss"]
@@ -18,6 +20,7 @@ class Url(Generic[ProtocolT]):
         if protocol is not None and protocol not in allowed_proto:
             raise ValueError(f"Unknown protocol: `{protocol}`, allowed: {allowed_proto}")
 
+        target_protocol: str = protocol or self._default_protocol()
         if isinstance(url, Url):
             self.__protocol: str = url.__protocol
             self.__address: str = url.__address
@@ -78,6 +81,14 @@ class Url(Generic[ProtocolT]):
         """
         return [""]
 
+    @classmethod
+    def factory(cls, *, port: int = 0, address: str = "127.0.0.1") -> Self:
+        return cls((f"{cls._default_protocol()}://" if cls._default_protocol() else "") + f"{address}:{port}")
+
+    @classmethod
+    def _default_protocol(cls) -> str:
+        return cls._allowed_protocols()[0]
+
 
 class HttpUrl(Url[HttpProtocolT]):
     @classmethod
diff --git a/beekeepy/beekeepy/_executable/__init__.py b/beekeepy/beekeepy/_executable/__init__.py
index a734003e..6b444e5d 100644
--- a/beekeepy/beekeepy/_executable/__init__.py
+++ b/beekeepy/beekeepy/_executable/__init__.py
@@ -1,13 +1,29 @@
 from __future__ import annotations
 
-from beekeepy._executable.arguments.arguments import Arguments
-from beekeepy._executable.arguments.beekeeper_arguments import BeekeeperArguments
+from beekeepy._executable.abc import (
+    Arguments,
+    ArgumentT,
+    Config,
+    ConfigT,
+    Executable,
+    StreamRepresentation,
+    StreamsHolder,
+)
+from beekeepy._executable.beekeeper_arguments import BeekeeperArguments
+from beekeepy._executable.beekeeper_config import BeekeeperConfig
 from beekeepy._executable.beekeeper_executable import BeekeeperExecutable
-from beekeepy._executable.executable import Executable
+from beekeepy._utilities.key_pair import KeyPair
 
 __all__ = [
-    "Arguments",
+    "BeekeeperConfig",
     "BeekeeperArguments",
     "BeekeeperExecutable",
+    "Arguments",
     "Executable",
+    "Config",
+    "StreamRepresentation",
+    "StreamsHolder",
+    "ArgumentT",
+    "ConfigT",
+    "KeyPair",
 ]
diff --git a/beekeepy/beekeepy/_executable/abc/__init__.py b/beekeepy/beekeepy/_executable/abc/__init__.py
new file mode 100644
index 00000000..57c9222f
--- /dev/null
+++ b/beekeepy/beekeepy/_executable/abc/__init__.py
@@ -0,0 +1,16 @@
+from __future__ import annotations
+
+from beekeepy._executable.abc.arguments import Arguments
+from beekeepy._executable.abc.config import Config
+from beekeepy._executable.abc.executable import ArgumentT, ConfigT, Executable
+from beekeepy._executable.abc.streams import StreamRepresentation, StreamsHolder
+
+__all__ = [
+    "Arguments",
+    "Executable",
+    "StreamsHolder",
+    "StreamRepresentation",
+    "Config",
+    "ArgumentT",
+    "ConfigT",
+]
diff --git a/beekeepy/beekeepy/_executable/abc/arguments.py b/beekeepy/beekeepy/_executable/abc/arguments.py
new file mode 100644
index 00000000..2fe6085a
--- /dev/null
+++ b/beekeepy/beekeepy/_executable/abc/arguments.py
@@ -0,0 +1,144 @@
+from __future__ import annotations
+
+from abc import ABC, abstractmethod
+from pathlib import Path
+from typing import TYPE_CHECKING, Any
+
+from pydantic import Field
+
+from schemas._preconfigured_base_model import PreconfiguredBaseModel
+
+if TYPE_CHECKING:
+    from typing_extensions import Self
+
+
+__all__ = ["Arguments"]
+
+
+class CliParser:
+    @classmethod
+    def parse_cli_input(cls, cli: list[str]) -> dict[str, str | list[str] | bool]:
+        ordered_cli: dict[str, str | set[str] | None] = {}
+        previous_key: str | None = None
+        for item in cli:
+            key, value = cls._preprocess_option(item)
+            if key.startswith("__"):
+                previous_key = key[2:]
+                ordered_cli[previous_key] = value
+                continue
+            if key.startswith("_"):
+                previous_key = key[1:]
+                ordered_cli[previous_key] = value
+                continue
+
+            if key in ordered_cli:
+                if isinstance((dict_value := ordered_cli[key]), set):
+                    dict_value.add(item)
+                elif ordered_cli[key] is None:
+                    assert isinstance(item, str), "parsing failed, item is not string"  # mypy check
+                    ordered_cli[key] = item
+                else:  # if ordered_cli[key] is not None and is not set
+                    assert isinstance(item, str), "parsing failed, item is not string"  # mypy check
+                    dict_value = ordered_cli[key]
+                    assert isinstance(dict_value, str), "parsing failed, dict_value is not string"  # mypy check
+                    ordered_cli[key] = {dict_value, item}
+                continue
+
+            assert (
+                previous_key is not None
+            ), "parsing failed, previous_key was not set and following argument is not prefixed"
+            ordered_cli[previous_key] = item
+            previous_key = None
+
+        return cls._convert_sets_to_lists_and_none_to_boolean(ordered_cli)
+
+    @classmethod
+    def _convert_sets_to_lists_and_none_to_boolean(
+        cls, ordered_cli: dict[str, str | set[str] | None]
+    ) -> dict[str, str | list[str] | bool]:
+        result: dict[str, str | list[str] | bool] = {}
+        for key, value in ordered_cli.items():
+            if isinstance(value, set):
+                result[key] = list(value)
+            elif value is None:
+                result[key] = True
+            else:
+                result[key] = value
+        return result
+
+    @classmethod
+    def _preprocess_option(cls, item: str) -> tuple[str, str | None]:
+        key = item
+        value = None
+        if "=" in item:
+            key, value = item.split("=")
+
+        key = key.replace("-", "_")
+        return key, value
+
+
+class Arguments(PreconfiguredBaseModel, ABC):
+    help_: bool = Field(alias="help", default=False)
+    version: bool = False
+    dump_config: bool = False
+
+    class Config:
+        arbitrary_types_allowed = True
+
+    def __convert_member_name_to_cli_value(self, member_name: str) -> str:
+        return member_name.replace("_", "-")
+
+    def __convert_member_value_to_string(self, member_value: int | str | Path | Any) -> str:
+        if isinstance(member_value, bool):
+            return ""
+        if isinstance(member_value, str):
+            return member_value
+        if isinstance(member_value, int):
+            return str(member_value)
+        if isinstance(member_value, Path):
+            return member_value.as_posix()
+        if isinstance(result := self._convert_member_value_to_string_default(member_value=member_value), str):
+            return result
+        raise TypeError("Invalid type")
+
+    @abstractmethod
+    def _convert_member_value_to_string_default(self, member_value: Any) -> str | Any: ...
+
+    def __prepare_arguments(self, pattern: str) -> list[str]:
+        data = self.dict(by_alias=True, exclude_none=True, exclude_unset=True, exclude_defaults=True)
+        cli_arguments: list[str] = []
+        for k, v in data.items():
+            cli_arguments.append(pattern.format(self.__convert_member_name_to_cli_value(k)))
+            cli_arguments.append(self.__convert_member_value_to_string(v))
+        return cli_arguments
+
+    def process(self, *, with_prefix: bool = True) -> list[str]:
+        pattern = self._generate_argument_prefix(with_prefix=with_prefix)
+        return self.__prepare_arguments(pattern)
+
+    def _generate_argument_prefix(self, *, with_prefix: bool) -> str:
+        return "--{0}" if with_prefix else "{0}"
+
+    def update_with(self, other: Self | None) -> None:
+        if other is None:
+            return
+
+        for other_name, other_value in other.dict(exclude_unset=True, exclude_defaults=True, exclude_none=True).items():
+            assert isinstance(other_name, str), "Member name has to be string"
+            setattr(self, other_name, other_value)
+
+    @classmethod
+    def parse_cli_input(cls, cli: list[str]) -> Self:
+        return cls(**CliParser.parse_cli_input(cli))
+
+    @classmethod
+    def just_get_help(cls) -> Self:
+        return cls(help_=True)
+
+    @classmethod
+    def just_get_version(cls) -> Self:
+        return cls(version=True)
+
+    @classmethod
+    def just_dump_config(cls) -> Self:
+        return cls(dump_config=True)
diff --git a/beekeepy/beekeepy/_interface/config.py b/beekeepy/beekeepy/_executable/abc/config.py
similarity index 59%
rename from beekeepy/beekeepy/_interface/config.py
rename to beekeepy/beekeepy/_executable/abc/config.py
index 4ae8f183..54d1bf42 100644
--- a/beekeepy/beekeepy/_interface/config.py
+++ b/beekeepy/beekeepy/_executable/abc/config.py
@@ -4,9 +4,10 @@ from pathlib import Path
 from types import UnionType
 from typing import TYPE_CHECKING, Any, ClassVar, get_args
 
+from loguru import logger
 from pydantic import BaseModel
 
-from beekeepy._interface.url import Url
+from beekeepy._communication import Url
 from beekeepy.exceptions import InvalidOptionError
 
 if TYPE_CHECKING:
@@ -25,26 +26,30 @@ class Config(BaseModel):
             out_file.write("# config automatically generated by helpy\n")
             for member_name, member_value in self.__dict__.items():
                 if member_value is not None:
-                    out_file.write(
-                        f"{self._convert_member_name_to_config_name(member_name)}={self._convert_member_value_to_config_value(member_value)}\n"
-                    )
+                    if isinstance(member_value, list) and len(member_value) == 0:
+                        continue
+
+                    entry_name = self._convert_member_name_to_config_name(member_name)
+                    entry_value = self._convert_member_value_to_config_value(member_name, member_value)
+                    for value in [entry_value] if not isinstance(entry_value, list) else entry_value:
+                        out_file.write(f"{entry_name} = {value}\n")
 
     @classmethod
     def load(cls, source: Path) -> Self:
         source = source / Config.DEFAULT_FILE_NAME if source.is_dir() else source
         assert source.exists(), "Given file does not exists."
         fields = cls.__fields__
-        values_to_write = {}
+        values_to_write: dict[str, Any] = {}
         with source.open("rt", encoding="utf-8") as in_file:
             for line in in_file:
                 if (line := line.strip("\n")) and not line.startswith("#"):
                     config_name, config_value = line.split("=")
                     member_name = cls._convert_config_name_to_member_name(config_name)
                     member_type = fields[member_name].annotation
-                    if isinstance(member_type, UnionType) and get_args(member_type)[-1] == type(None):
+                    if isinstance(member_type, UnionType) and (type(None) in get_args(member_type)):
                         member_type = get_args(member_type)[0]
                     values_to_write[member_name] = cls._convert_config_value_to_member_value(
-                        config_value, expected=member_type
+                        config_value, expected=member_type, current_value=values_to_write.get(member_name)
                     )
         return cls(**values_to_write)
 
@@ -57,9 +62,9 @@ class Config(BaseModel):
         return config_name.strip().replace("-", "_")
 
     @classmethod
-    def _convert_member_value_to_config_value(cls, member_value: Any) -> str:
+    def _convert_member_value_to_config_value(cls, member_name: str, member_value: Any) -> str | list[str]:  # noqa: ARG003
         if isinstance(member_value, list):
-            return " ".join(member_value)
+            return member_value
 
         if isinstance(member_value, bool):
             return "yes" if member_value else "no"
@@ -73,8 +78,8 @@ class Config(BaseModel):
         return str(member_value)
 
     @classmethod
-    def _convert_config_value_to_member_value(  # noqa: PLR0911
-        cls, config_value: str, *, expected: type[Any]
+    def _convert_config_value_to_member_value(  # noqa: PLR0911, C901
+        cls, config_value: str, *, expected: type[Any], current_value: Any | None
     ) -> Any | None:
         config_value = config_value.strip()
         if config_value is None:
@@ -83,8 +88,21 @@ class Config(BaseModel):
         if expected == Path:
             return Path(config_value.replace('"', ""))
 
-        if expected == list[str]:
-            return config_value.split()
+        if issubclass(expected, list) or "list" in str(expected):
+            list_arg_t = get_args(expected)[0]
+            if len(get_args(list_arg_t)):  # in case of unions
+                list_arg_t = get_args(list_arg_t)[0]
+            logger.info(f"{list_arg_t=}")
+            values = [
+                cls._convert_config_value_to_member_value(value, expected=list_arg_t, current_value=None)
+                for value in config_value.split()
+            ]
+            if current_value is not None:
+                if isinstance(current_value, list):
+                    current_value.extend(values)
+                    return current_value
+                return [*values, current_value]
+            return values
 
         if expected == Url:
             return Url(config_value)
@@ -99,4 +117,10 @@ class Config(BaseModel):
 
             raise InvalidOptionError(f"Expected `yes` or `no`, got: `{config_value}`")
 
+        if "str" in str(expected):
+            return config_value.strip('"')
+
+        if isinstance(expected, type) and issubclass(expected, int | str) and hasattr(expected, "validate"):
+            return expected.validate(config_value)
+
         return expected(config_value) if expected is not None else None
diff --git a/beekeepy/beekeepy/_executable/executable.py b/beekeepy/beekeepy/_executable/abc/executable.py
similarity index 62%
rename from beekeepy/beekeepy/_executable/executable.py
rename to beekeepy/beekeepy/_executable/abc/executable.py
index 4f171554..476e3d10 100644
--- a/beekeepy/beekeepy/_executable/executable.py
+++ b/beekeepy/beekeepy/_executable/abc/executable.py
@@ -3,17 +3,21 @@ from __future__ import annotations
 import os
 import signal
 import subprocess
-import warnings
+import time
 from abc import ABC, abstractmethod
+from contextlib import contextmanager
 from typing import TYPE_CHECKING, Any, Generic, TypeVar
 
-from beekeepy._executable.arguments.arguments import Arguments
-from beekeepy._executable.streams import StreamsHolder
-from beekeepy._interface.config import Config
-from beekeepy._interface.context import ContextSync
-from beekeepy.exceptions import BeekeeperIsNotRunningError, TimeoutReachWhileCloseError
+import psutil
+
+from beekeepy._executable.abc.arguments import Arguments
+from beekeepy._executable.abc.config import Config
+from beekeepy._executable.abc.streams import StreamsHolder
+from beekeepy._utilities.context import ContextSync
+from beekeepy.exceptions import ExecutableIsNotRunningError, TimeoutReachWhileCloseError
 
 if TYPE_CHECKING:
+    from collections.abc import Iterator
     from pathlib import Path
 
     from loguru import Logger
@@ -21,7 +25,7 @@ if TYPE_CHECKING:
 
 class Closeable(ABC):
     @abstractmethod
-    def close(self) -> None: ...
+    def close(self, timeout_secs: float = 10.0) -> None: ...
 
 
 class AutoCloser(ContextSync[None]):
@@ -70,15 +74,41 @@ class Executable(Closeable, Generic[ConfigT, ArgumentT]):
     def config(self) -> ConfigT:
         return self.__config
 
-    def run(
+    @property
+    def arguments(self) -> ArgumentT:
+        return self.__arguments
+
+    @property
+    def executable_path(self) -> Path:
+        return self.__executable_path
+
+    def _run(
         self,
         *,
         blocking: bool,
-        arguments: ArgumentT | None = None,
         environ: dict[str, str] | None = None,
         propagate_sigint: bool = True,
+        save_config: bool = True,
     ) -> AutoCloser:
-        command, environment_variables = self.__prepare(arguments=arguments, environ=environ)
+        return self.__run(
+            blocking=blocking,
+            arguments=self.arguments,
+            environ=environ,
+            propagate_sigint=propagate_sigint,
+            save_config=save_config,
+        )
+
+    def __run(  # noqa: PLR0913
+        self,
+        *,
+        blocking: bool,
+        arguments: ArgumentT,
+        environ: dict[str, str] | None = None,
+        propagate_sigint: bool = True,
+        save_config: bool = True,
+    ) -> AutoCloser:
+        command, environment_variables = self.__prepare(arguments=arguments, environ=environ, save_config=save_config)
+        self._logger.info(f"starting `{self.__executable_path.stem}` as: `{command}`")
 
         if blocking:
             with self.__files.stdout as stdout, self.__files.stderr as stderr:
@@ -114,9 +144,11 @@ class Executable(Closeable, Generic[ConfigT, ArgumentT]):
         return result.decode().strip()
 
     def __prepare(
-        self, arguments: ArgumentT | None, environ: dict[str, str] | None
+        self,
+        arguments: ArgumentT,
+        environ: dict[str, str] | None,
+        save_config: bool = True,  # noqa: FBT001, FBT002
     ) -> tuple[list[str], dict[str, str]]:
-        arguments = arguments or self.__arguments
         environ = environ or {}
 
         self.__working_directory.mkdir(exist_ok=True)
@@ -127,7 +159,8 @@ class Executable(Closeable, Generic[ConfigT, ArgumentT]):
 
         environment_variables = dict(os.environ)
         environment_variables.update(environ)
-        self.config.save(self.working_directory)
+        if save_config:
+            self.config.save(self.working_directory)
 
         return command, environment_variables
 
@@ -136,7 +169,7 @@ class Executable(Closeable, Generic[ConfigT, ArgumentT]):
 
     def detach(self) -> int:
         if self.__process is None:
-            raise BeekeeperIsNotRunningError
+            raise ExecutableIsNotRunningError
         pid = self.pid
         self.__process = None
         self.__files.close()
@@ -158,16 +191,6 @@ class Executable(Closeable, Generic[ConfigT, ArgumentT]):
             self.__process = None
             self.__files.close()
 
-    def __warn_if_pid_files_exists(self) -> None:
-        if self.__pid_files_exists():
-            warnings.warn(
-                f"PID file has not been removed, malfunction may occur. Working directory: {self.working_directory}",
-                stacklevel=2,
-            )
-
-    def __pid_files_exists(self) -> bool:
-        return len(list(self.working_directory.glob("*.pid"))) > 0
-
     def is_running(self) -> bool:
         if not self.__process:
             return False
@@ -177,6 +200,17 @@ class Executable(Closeable, Generic[ConfigT, ArgumentT]):
     def log_has_phrase(self, text: str) -> bool:
         return text in self.__files
 
+    @contextmanager
+    def restore_arguments(self, new_arguments: ArgumentT | None) -> Iterator[None]:
+        __backup = self.__arguments
+        self.__arguments = new_arguments or self.__arguments
+        try:
+            yield
+        except:  # noqa: TRY302 # https://docs.python.org/3/library/contextlib.html#contextlib.contextmanager
+            raise
+        finally:
+            self.__arguments = __backup
+
     @abstractmethod
     def _construct_config(self) -> ConfigT: ...
 
@@ -184,9 +218,20 @@ class Executable(Closeable, Generic[ConfigT, ArgumentT]):
     def _construct_arguments(self) -> ArgumentT: ...
 
     def generate_default_config(self) -> ConfigT:
-        path_to_config = self.working_directory / (Config.DEFAULT_FILE_NAME)
-        self.run(blocking=True, arguments=self.__arguments.just_dump_config())
-        temp_path_to_file = path_to_config.rename(Config.DEFAULT_FILE_NAME + ".tmp")
+        if not self.working_directory.exists():
+            self.working_directory.mkdir(parents=True)
+        orig_path_to_config: Path | None = None
+        path_to_config = self.working_directory / Config.DEFAULT_FILE_NAME
+        if path_to_config.exists():
+            orig_path_to_config = path_to_config.rename(
+                path_to_config.with_suffix(".ini.orig")
+            )  # temporary move it to not interfere with config generation
+        arguments = self._construct_arguments()
+        arguments.dump_config = True
+        self.__run(blocking=True, arguments=arguments, save_config=False)
+        temp_path_to_file = path_to_config.rename(path_to_config.with_suffix(".ini.tmp"))
+        if orig_path_to_config is not None:
+            orig_path_to_config.rename(path_to_config)
         return self.config.load(temp_path_to_file)
 
     def get_help_text(self) -> str:
@@ -194,3 +239,13 @@ class Executable(Closeable, Generic[ConfigT, ArgumentT]):
 
     def version(self) -> str:
         return self.run_and_get_output(arguments=self.__arguments.just_get_version())
+
+    def reserved_ports(self, *, timeout_seconds: int = 10) -> list[int]:
+        assert self.is_running(), "Cannot obtain reserved ports for not started executable"
+        start = time.perf_counter()
+        while start + timeout_seconds >= time.perf_counter():
+            connections = psutil.net_connections("inet4")
+            reserved_ports = [connection.laddr[1] for connection in connections if connection.pid == self.pid]  # type: ignore[misc]
+            if reserved_ports:
+                return reserved_ports
+        raise TimeoutError
diff --git a/beekeepy/beekeepy/_executable/streams.py b/beekeepy/beekeepy/_executable/abc/streams.py
similarity index 98%
rename from beekeepy/beekeepy/_executable/streams.py
rename to beekeepy/beekeepy/_executable/abc/streams.py
index 27b5eef1..1e615284 100644
--- a/beekeepy/beekeepy/_executable/streams.py
+++ b/beekeepy/beekeepy/_executable/abc/streams.py
@@ -3,7 +3,7 @@ from __future__ import annotations
 from dataclasses import dataclass, field
 from typing import TYPE_CHECKING, TextIO, cast
 
-from beekeepy._interface.context import ContextSync
+from beekeepy._utilities.context import ContextSync
 
 if TYPE_CHECKING:
     from pathlib import Path
diff --git a/beekeepy/beekeepy/_executable/arguments/arguments.py b/beekeepy/beekeepy/_executable/arguments/arguments.py
deleted file mode 100644
index 4a248218..00000000
--- a/beekeepy/beekeepy/_executable/arguments/arguments.py
+++ /dev/null
@@ -1,64 +0,0 @@
-from __future__ import annotations
-
-from abc import ABC, abstractmethod
-from pathlib import Path
-from typing import TYPE_CHECKING, Any
-
-from pydantic import Field
-
-from schemas._preconfigured_base_model import PreconfiguredBaseModel
-
-if TYPE_CHECKING:
-    from typing_extensions import Self
-
-
-class Arguments(PreconfiguredBaseModel, ABC):
-    help_: bool = Field(alias="help", default=False)
-    version: bool = False
-    dump_config: bool = False
-
-    class Config:
-        arbitrary_types_allowed = True
-
-    def __convert_member_name_to_cli_value(self, member_name: str) -> str:
-        return member_name.replace("_", "-")
-
-    def __convert_member_value_to_string(self, member_value: int | str | Path | Any) -> str:
-        if isinstance(member_value, bool):
-            return ""
-        if isinstance(member_value, str):
-            return member_value
-        if isinstance(member_value, int):
-            return str(member_value)
-        if isinstance(member_value, Path):
-            return member_value.as_posix()
-        if isinstance(result := self._convert_member_value_to_string_default(member_value=member_value), str):
-            return result
-        raise TypeError("Invalid type")
-
-    @abstractmethod
-    def _convert_member_value_to_string_default(self, member_value: Any) -> str | Any: ...
-
-    def __prepare_arguments(self, pattern: str) -> list[str]:
-        data = self.dict(by_alias=True, exclude_none=True, exclude_unset=True, exclude_defaults=True)
-        cli_arguments: list[str] = []
-        for k, v in data.items():
-            cli_arguments.append(pattern.format(self.__convert_member_name_to_cli_value(k)))
-            cli_arguments.append(self.__convert_member_value_to_string(v))
-        return cli_arguments
-
-    def process(self, *, with_prefix: bool = True) -> list[str]:
-        pattern = "--{0}" if with_prefix else "{0}"
-        return self.__prepare_arguments(pattern)
-
-    @classmethod
-    def just_get_help(cls) -> Self:
-        return cls(help_=True)
-
-    @classmethod
-    def just_get_version(cls) -> Self:
-        return cls(version=True)
-
-    @classmethod
-    def just_dump_config(cls) -> Self:
-        return cls(dump_config=True)
diff --git a/beekeepy/beekeepy/_executable/arguments/beekeeper_arguments.py b/beekeepy/beekeepy/_executable/beekeeper_arguments.py
similarity index 82%
rename from beekeepy/beekeepy/_executable/arguments/beekeeper_arguments.py
rename to beekeepy/beekeepy/_executable/beekeeper_arguments.py
index 525fde4f..5af2501b 100644
--- a/beekeepy/beekeepy/_executable/arguments/beekeeper_arguments.py
+++ b/beekeepy/beekeepy/_executable/beekeeper_arguments.py
@@ -4,16 +4,14 @@ from dataclasses import dataclass
 from pathlib import Path
 from typing import Any, ClassVar, Literal
 
-from beekeepy._executable.arguments.arguments import Arguments
-from beekeepy._interface.url import HttpUrl
+from beekeepy._communication import HttpUrl
+from beekeepy._executable.abc.arguments import Arguments
 
 
 class BeekeeperArgumentsDefaults:
     DEFAULT_BACKTRACE: ClassVar[Literal["yes", "no"]] = "yes"
-    DEFAULT_DATA_DIR: ClassVar[Path] = Path.cwd()
     DEFAULT_EXPORT_KEYS_WALLET: ClassVar[ExportKeysWalletParams | None] = None
     DEFAULT_LOG_JSON_RPC: ClassVar[Path | None] = None
-    DEFAULT_NOTIFICATIONS_ENDPOINT: ClassVar[HttpUrl | None] = None
     DEFAULT_UNLOCK_TIMEOUT: ClassVar[int] = 900
     DEFAULT_UNLOCK_INTERVAL: ClassVar[int] = 500
     DEFAULT_WALLET_DIR: ClassVar[Path] = Path.cwd()
@@ -29,10 +27,9 @@ class ExportKeysWalletParams:
 
 class BeekeeperArguments(Arguments):
     backtrace: Literal["yes", "no"] | None = BeekeeperArgumentsDefaults.DEFAULT_BACKTRACE
-    data_dir: Path = BeekeeperArgumentsDefaults.DEFAULT_DATA_DIR
+    data_dir: Path | None = None
     export_keys_wallet: ExportKeysWalletParams | None = BeekeeperArgumentsDefaults.DEFAULT_EXPORT_KEYS_WALLET
     log_json_rpc: Path | None = BeekeeperArgumentsDefaults.DEFAULT_LOG_JSON_RPC
-    notifications_endpoint: HttpUrl | None = BeekeeperArgumentsDefaults.DEFAULT_NOTIFICATIONS_ENDPOINT
     unlock_timeout: int | None = BeekeeperArgumentsDefaults.DEFAULT_UNLOCK_TIMEOUT
     wallet_dir: Path | None = BeekeeperArgumentsDefaults.DEFAULT_WALLET_DIR
     webserver_thread_pool_size: int | None = BeekeeperArgumentsDefaults.DEFAULT_WEBSERVER_THREAD_POOL_SIZE
diff --git a/beekeepy/beekeepy/_executable/beekeeper_config.py b/beekeepy/beekeepy/_executable/beekeeper_config.py
index 904ebb7c..8dd7fdac 100644
--- a/beekeepy/beekeepy/_executable/beekeeper_config.py
+++ b/beekeepy/beekeepy/_executable/beekeeper_config.py
@@ -4,9 +4,9 @@ from pathlib import Path  # noqa: TCH003
 
 from pydantic import Field
 
+from beekeepy._communication import HttpUrl, WsUrl
+from beekeepy._executable.abc.config import Config
 from beekeepy._executable.defaults import BeekeeperDefaults, ExportKeysWalletParams
-from beekeepy._interface.config import Config
-from beekeepy._interface.url import HttpUrl, WsUrl
 
 
 def http_webserver_default() -> HttpUrl:
@@ -23,7 +23,6 @@ class BeekeeperConfig(Config):
     webserver_ws_endpoint: WsUrl | None = None
     webserver_ws_deflate: int = 0
     webserver_thread_pool_size: int = 1
-    notifications_endpoint: HttpUrl | None = BeekeeperDefaults.DEFAULT_NOTIFICATIONS_ENDPOINT
     backtrace: str = BeekeeperDefaults.DEFAULT_BACKTRACE
     plugin: list[str] = Field(default_factory=lambda: ["json_rpc", "webserver"])
     export_keys_wallet: ExportKeysWalletParams | None = BeekeeperDefaults.DEFAULT_EXPORT_KEYS_WALLET
diff --git a/beekeepy/beekeepy/_executable/beekeeper_executable.py b/beekeepy/beekeepy/_executable/beekeeper_executable.py
index 38ebcf1b..e710c947 100644
--- a/beekeepy/beekeepy/_executable/beekeeper_executable.py
+++ b/beekeepy/beekeepy/_executable/beekeeper_executable.py
@@ -6,26 +6,24 @@ import tempfile
 from pathlib import Path
 from typing import TYPE_CHECKING
 
-from beekeepy._executable.arguments.beekeeper_arguments import BeekeeperArguments, ExportKeysWalletParams
+from beekeepy._executable.abc.executable import AutoCloser, Executable
+from beekeepy._executable.beekeeper_arguments import BeekeeperArguments, ExportKeysWalletParams
 from beekeepy._executable.beekeeper_config import BeekeeperConfig
 from beekeepy._executable.beekeeper_executable_discovery import get_beekeeper_binary_path
-from beekeepy._executable.executable import Executable
-from beekeepy._interface.key_pair import KeyPair
-from beekeepy._interface.url import HttpUrl
-from beekeepy._runnable_handle.settings import Settings
+from beekeepy._utilities.key_pair import KeyPair
 
 if TYPE_CHECKING:
     from loguru import Logger
 
 
 class BeekeeperExecutable(Executable[BeekeeperConfig, BeekeeperArguments]):
-    def __init__(self, settings: Settings, logger: Logger) -> None:
-        super().__init__(
-            settings.binary_path or get_beekeeper_binary_path(), settings.ensured_working_directory, logger
-        )
+    def __init__(self, executable_path: Path | None, working_directory: Path, logger: Logger) -> None:
+        super().__init__(executable_path or get_beekeeper_binary_path(), working_directory, logger)
 
     def _construct_config(self) -> BeekeeperConfig:
-        return BeekeeperConfig(wallet_dir=self.working_directory)
+        config = BeekeeperConfig(wallet_dir=self.working_directory)
+        config.plugin.append("app_status_api")
+        return config
 
     def _construct_arguments(self) -> BeekeeperArguments:
         return BeekeeperArguments(data_dir=self.working_directory)
@@ -38,17 +36,19 @@ class BeekeeperExecutable(Executable[BeekeeperConfig, BeekeeperArguments]):
             wallet_file_name = f"{wallet_name}.wallet"
             shutil.copyfile(self.working_directory / wallet_file_name, tempdir_path / wallet_file_name)
             bk = BeekeeperExecutable(
-                settings=Settings(binary_path=get_beekeeper_binary_path(), working_directory=self.working_directory),
+                executable_path=self.executable_path,
+                working_directory=self.working_directory,
                 logger=self._logger,
             )
-            bk.run(
-                blocking=True,
-                arguments=BeekeeperArguments(
+            with bk.restore_arguments(
+                BeekeeperArguments(
                     data_dir=tempdir_path,
-                    notifications_endpoint=HttpUrl("0.0.0.0:0"),
                     export_keys_wallet=ExportKeysWalletParams(wallet_name=wallet_name, wallet_password=wallet_password),
-                ),
-            )
+                )
+            ):
+                bk.run(
+                    blocking=True,
+                )
 
         keys_path = bk.working_directory / f"{wallet_name}.keys"
         if extract_to is not None:
@@ -57,3 +57,12 @@ class BeekeeperExecutable(Executable[BeekeeperConfig, BeekeeperArguments]):
 
         with keys_path.open("r") as file:
             return [KeyPair(**obj) for obj in json.load(file)]
+
+    def run(
+        self,
+        *,
+        blocking: bool,
+        environ: dict[str, str] | None = None,
+        propagate_sigint: bool = True,
+    ) -> AutoCloser:
+        return self._run(blocking=blocking, environ=environ, propagate_sigint=propagate_sigint)
diff --git a/beekeepy/beekeepy/_executable/defaults.py b/beekeepy/beekeepy/_executable/defaults.py
index c3f2f5d5..894b2a37 100644
--- a/beekeepy/beekeepy/_executable/defaults.py
+++ b/beekeepy/beekeepy/_executable/defaults.py
@@ -6,7 +6,7 @@ from typing import ClassVar
 
 from pydantic import BaseModel
 
-from beekeepy._interface.url import HttpUrl  # noqa: TCH001
+from beekeepy._communication import HttpUrl  # noqa: TCH001
 
 
 @dataclass
@@ -20,7 +20,6 @@ class BeekeeperDefaults(BaseModel):
     DEFAULT_DATA_DIR: ClassVar[Path] = Path.cwd()
     DEFAULT_EXPORT_KEYS_WALLET: ClassVar[ExportKeysWalletParams | None] = None
     DEFAULT_LOG_JSON_RPC: ClassVar[Path | None] = None
-    DEFAULT_NOTIFICATIONS_ENDPOINT: ClassVar[HttpUrl | None] = None
     DEFAULT_UNLOCK_TIMEOUT: ClassVar[int] = 900
     DEFAULT_UNLOCK_INTERVAL: ClassVar[int] = 500
     DEFAULT_WALLET_DIR: ClassVar[Path] = Path.cwd()
diff --git a/beekeepy/beekeepy/_interface/__init__.py b/beekeepy/beekeepy/_interface/__init__.py
index e69de29b..1a826ee5 100644
--- a/beekeepy/beekeepy/_interface/__init__.py
+++ b/beekeepy/beekeepy/_interface/__init__.py
@@ -0,0 +1,24 @@
+from __future__ import annotations
+
+from beekeepy._interface import abc
+from beekeepy._interface.asynchronous.beekeeper import Beekeeper as AsyncBeekeper
+from beekeepy._interface.asynchronous.session import Session as AsyncSession
+from beekeepy._interface.asynchronous.wallet import UnlockedWallet as AsyncUnlockedWallet
+from beekeepy._interface.asynchronous.wallet import Wallet as AsyncWallet
+from beekeepy._interface.settings import InterfaceSettings
+from beekeepy._interface.synchronous.beekeeper import Beekeeper
+from beekeepy._interface.synchronous.session import Session
+from beekeepy._interface.synchronous.wallet import UnlockedWallet, Wallet
+
+__all__ = [
+    "abc",
+    "AsyncBeekeper",
+    "AsyncSession",
+    "AsyncUnlockedWallet",
+    "AsyncWallet",
+    "Beekeeper",
+    "InterfaceSettings",
+    "Session",
+    "UnlockedWallet",
+    "Wallet",
+]
diff --git a/beekeepy/beekeepy/_interface/abc/__init__.py b/beekeepy/beekeepy/_interface/abc/__init__.py
index e69de29b..b73bcd06 100644
--- a/beekeepy/beekeepy/_interface/abc/__init__.py
+++ b/beekeepy/beekeepy/_interface/abc/__init__.py
@@ -0,0 +1,23 @@
+from __future__ import annotations
+
+from beekeepy._interface.abc.asynchronous.beekeeper import Beekeeper as AsyncBeekeeper
+from beekeepy._interface.abc.asynchronous.session import Session as AsyncSession
+from beekeepy._interface.abc.asynchronous.wallet import UnlockedWallet as AsyncUnlockedWallet
+from beekeepy._interface.abc.asynchronous.wallet import Wallet as AsyncWallet
+from beekeepy._interface.abc.packed_object import PackedAsyncBeekeeper, PackedSyncBeekeeper
+from beekeepy._interface.abc.synchronous.beekeeper import Beekeeper
+from beekeepy._interface.abc.synchronous.session import Session
+from beekeepy._interface.abc.synchronous.wallet import UnlockedWallet, Wallet
+
+__all__ = [
+    "AsyncBeekeeper",
+    "AsyncSession",
+    "AsyncUnlockedWallet",
+    "AsyncWallet",
+    "Beekeeper",
+    "PackedAsyncBeekeeper",
+    "PackedSyncBeekeeper",
+    "Session",
+    "UnlockedWallet",
+    "Wallet",
+]
diff --git a/beekeepy/beekeepy/_interface/abc/asynchronous/beekeeper.py b/beekeepy/beekeepy/_interface/abc/asynchronous/beekeeper.py
index f1509340..0604b7bc 100644
--- a/beekeepy/beekeepy/_interface/abc/asynchronous/beekeeper.py
+++ b/beekeepy/beekeepy/_interface/abc/asynchronous/beekeeper.py
@@ -3,15 +3,15 @@ from __future__ import annotations
 from abc import ABC, abstractmethod
 from typing import TYPE_CHECKING, cast
 
-from beekeepy._communication.settings import CommunicationSettings
-from beekeepy._interface.context import ContextAsync
-from beekeepy._interface.context_settings_updater import ContextSettingsUpdater
-from beekeepy._runnable_handle.settings import Settings
+from beekeepy._communication import CommunicationSettings
+from beekeepy._interface.settings import InterfaceSettings
+from beekeepy._utilities.context import ContextAsync
+from beekeepy._utilities.context_settings_updater import ContextSettingsUpdater
 
 if TYPE_CHECKING:
+    from beekeepy._communication import HttpUrl
     from beekeepy._interface.abc.asynchronous.session import Session
     from beekeepy._interface.abc.packed_object import PackedAsyncBeekeeper
-    from beekeepy._interface.url import HttpUrl
 
 
 class Beekeeper(ContextAsync["Beekeeper"], ContextSettingsUpdater[CommunicationSettings], ABC):
@@ -19,9 +19,9 @@ class Beekeeper(ContextAsync["Beekeeper"], ContextSettingsUpdater[CommunicationS
     async def create_session(self, *, salt: str | None = None) -> Session: ...
 
     @property
-    def settings(self) -> Settings:
+    def settings(self) -> InterfaceSettings:
         """Returns read-only settings."""
-        return cast(Settings, self._get_copy_of_settings())
+        return cast(InterfaceSettings, self._get_copy_of_settings())
 
     @property
     def http_endpoint(self) -> HttpUrl:
@@ -42,13 +42,13 @@ class Beekeeper(ContextAsync["Beekeeper"], ContextSettingsUpdater[CommunicationS
         """Detaches process and returns PID."""
 
     @classmethod
-    async def factory(cls, *, settings: Settings | None = None) -> Beekeeper:
+    async def factory(cls, *, settings: InterfaceSettings | None = None) -> Beekeeper:
         from beekeepy._interface.asynchronous.beekeeper import Beekeeper as BeekeeperImplementation
 
         return await BeekeeperImplementation._factory(settings=settings)
 
     @classmethod
-    async def remote_factory(cls, *, url_or_settings: Settings | HttpUrl) -> Beekeeper:
+    async def remote_factory(cls, *, url_or_settings: InterfaceSettings | HttpUrl) -> Beekeeper:
         from beekeepy._interface.asynchronous.beekeeper import Beekeeper as BeekeeperImplementation
 
         return await BeekeeperImplementation._remote_factory(url_or_settings=url_or_settings)
diff --git a/beekeepy/beekeepy/_interface/abc/asynchronous/session.py b/beekeepy/beekeepy/_interface/abc/asynchronous/session.py
index befad86f..8d2c2e93 100644
--- a/beekeepy/beekeepy/_interface/abc/asynchronous/session.py
+++ b/beekeepy/beekeepy/_interface/abc/asynchronous/session.py
@@ -3,7 +3,7 @@ from __future__ import annotations
 from abc import ABC, abstractmethod
 from typing import TYPE_CHECKING, TypeAlias, overload
 
-from beekeepy._interface.context import ContextAsync
+from beekeepy._utilities.context import ContextAsync
 
 if TYPE_CHECKING:
     from beekeepy._interface.abc.asynchronous.wallet import UnlockedWallet, Wallet
diff --git a/beekeepy/beekeepy/_interface/abc/asynchronous/wallet.py b/beekeepy/beekeepy/_interface/abc/asynchronous/wallet.py
index f251972e..27726dde 100644
--- a/beekeepy/beekeepy/_interface/abc/asynchronous/wallet.py
+++ b/beekeepy/beekeepy/_interface/abc/asynchronous/wallet.py
@@ -3,8 +3,8 @@ from __future__ import annotations
 from abc import ABC, abstractmethod
 from typing import TYPE_CHECKING
 
-from beekeepy._interface.context import ContextAsync
 from beekeepy._interface.wallets_common import ContainsWalletName
+from beekeepy._utilities.context import ContextAsync
 
 if TYPE_CHECKING:
     from datetime import datetime
diff --git a/beekeepy/beekeepy/_interface/abc/packed_object.py b/beekeepy/beekeepy/_interface/abc/packed_object.py
index c92f7ddb..5ba364b8 100644
--- a/beekeepy/beekeepy/_interface/abc/packed_object.py
+++ b/beekeepy/beekeepy/_interface/abc/packed_object.py
@@ -2,17 +2,17 @@ from __future__ import annotations
 
 from typing import TYPE_CHECKING, Generic, Protocol, TypeVar
 
-from beekeepy._interface.settings_holder import UniqueSettingsHolder
-from beekeepy._runnable_handle.settings import Settings
+from beekeepy._interface.settings import InterfaceSettings
+from beekeepy._utilities.settings_holder import UniqueSettingsHolder
 
 if TYPE_CHECKING:
+    from beekeepy._communication import HttpUrl
     from beekeepy._interface.abc.asynchronous.beekeeper import (
         Beekeeper as AsynchronousBeekeeperInterface,
     )
     from beekeepy._interface.abc.synchronous.beekeeper import (
         Beekeeper as SynchronousBeekeeperInterface,
     )
-    from beekeepy._interface.url import HttpUrl
 
 __all__ = [
     "PackedSyncBeekeeper",
@@ -21,27 +21,22 @@ __all__ = [
 
 
 class _SyncRemoteFactoryCallable(Protocol):
-    def __call__(self, *, url_or_settings: HttpUrl | Settings) -> SynchronousBeekeeperInterface: ...
+    def __call__(self, *, url_or_settings: HttpUrl | InterfaceSettings) -> SynchronousBeekeeperInterface: ...
 
 
 class _AsyncRemoteFactoryCallable(Protocol):
-    async def __call__(self, *, url_or_settings: HttpUrl | Settings) -> AsynchronousBeekeeperInterface: ...
+    async def __call__(self, *, url_or_settings: HttpUrl | InterfaceSettings) -> AsynchronousBeekeeperInterface: ...
 
 
 FactoryT = TypeVar("FactoryT", bound=_SyncRemoteFactoryCallable | _AsyncRemoteFactoryCallable)
 
 
-class Packed(UniqueSettingsHolder[Settings], Generic[FactoryT]):
+class Packed(UniqueSettingsHolder[InterfaceSettings], Generic[FactoryT]):
     """Allows to transfer beekeeper handle to other process."""
 
-    def __init__(self, settings: Settings, unpack_factory: FactoryT) -> None:
+    def __init__(self, settings: InterfaceSettings, unpack_factory: FactoryT) -> None:
         super().__init__(settings=settings)
         self._unpack_factory = unpack_factory
-        self._prepare_settings_for_packing()
-
-    def _prepare_settings_for_packing(self) -> None:
-        with self.update_settings() as settings:
-            settings.notification_endpoint = None
 
 
 class PackedSyncBeekeeper(Packed[_SyncRemoteFactoryCallable]):
diff --git a/beekeepy/beekeepy/_interface/abc/synchronous/beekeeper.py b/beekeepy/beekeepy/_interface/abc/synchronous/beekeeper.py
index 22febafd..a8068fb2 100644
--- a/beekeepy/beekeepy/_interface/abc/synchronous/beekeeper.py
+++ b/beekeepy/beekeepy/_interface/abc/synchronous/beekeeper.py
@@ -3,15 +3,15 @@ from __future__ import annotations
 from abc import ABC, abstractmethod
 from typing import TYPE_CHECKING, cast
 
-from beekeepy._communication.settings import CommunicationSettings
-from beekeepy._interface.context import ContextSync
-from beekeepy._interface.context_settings_updater import ContextSettingsUpdater
-from beekeepy._runnable_handle.settings import Settings
+from beekeepy._communication import CommunicationSettings
+from beekeepy._interface.settings import InterfaceSettings
+from beekeepy._utilities.context import ContextSync
+from beekeepy._utilities.context_settings_updater import ContextSettingsUpdater
 
 if TYPE_CHECKING:
+    from beekeepy._communication import HttpUrl
     from beekeepy._interface.abc.packed_object import PackedSyncBeekeeper
     from beekeepy._interface.abc.synchronous.session import Session
-    from beekeepy._interface.url import HttpUrl
 
 
 class Beekeeper(ContextSync["Beekeeper"], ContextSettingsUpdater[CommunicationSettings], ABC):
@@ -19,9 +19,9 @@ class Beekeeper(ContextSync["Beekeeper"], ContextSettingsUpdater[CommunicationSe
     def create_session(self, *, salt: str | None = None) -> Session: ...
 
     @property
-    def settings(self) -> Settings:
+    def settings(self) -> InterfaceSettings:
         """Returns read-only settings."""
-        return cast(Settings, self._get_copy_of_settings())
+        return cast(InterfaceSettings, self._get_copy_of_settings())
 
     @property
     def http_endpoint(self) -> HttpUrl:
@@ -42,13 +42,13 @@ class Beekeeper(ContextSync["Beekeeper"], ContextSettingsUpdater[CommunicationSe
         """Detaches process and returns PID."""
 
     @classmethod
-    def factory(cls, *, settings: Settings | None = None) -> Beekeeper:
+    def factory(cls, *, settings: InterfaceSettings | None = None) -> Beekeeper:
         from beekeepy._interface.synchronous.beekeeper import Beekeeper as BeekeeperImplementation
 
         return BeekeeperImplementation._factory(settings=settings)
 
     @classmethod
-    def remote_factory(cls, *, url_or_settings: Settings | HttpUrl) -> Beekeeper:
+    def remote_factory(cls, *, url_or_settings: InterfaceSettings | HttpUrl) -> Beekeeper:
         from beekeepy._interface.synchronous.beekeeper import Beekeeper as BeekeeperImplementation
 
         return BeekeeperImplementation._remote_factory(url_or_settings=url_or_settings)
diff --git a/beekeepy/beekeepy/_interface/abc/synchronous/session.py b/beekeepy/beekeepy/_interface/abc/synchronous/session.py
index 458b9640..2292259b 100644
--- a/beekeepy/beekeepy/_interface/abc/synchronous/session.py
+++ b/beekeepy/beekeepy/_interface/abc/synchronous/session.py
@@ -3,7 +3,7 @@ from __future__ import annotations
 from abc import ABC, abstractmethod
 from typing import TYPE_CHECKING, TypeAlias, overload
 
-from beekeepy._interface.context import ContextSync
+from beekeepy._utilities.context import ContextSync
 
 if TYPE_CHECKING:
     from beekeepy._interface.abc.synchronous.wallet import UnlockedWallet, Wallet
diff --git a/beekeepy/beekeepy/_interface/abc/synchronous/wallet.py b/beekeepy/beekeepy/_interface/abc/synchronous/wallet.py
index 31f07bec..51895086 100644
--- a/beekeepy/beekeepy/_interface/abc/synchronous/wallet.py
+++ b/beekeepy/beekeepy/_interface/abc/synchronous/wallet.py
@@ -3,8 +3,8 @@ from __future__ import annotations
 from abc import ABC, abstractmethod
 from typing import TYPE_CHECKING
 
-from beekeepy._interface.context import ContextSync
 from beekeepy._interface.wallets_common import ContainsWalletName
+from beekeepy._utilities.context import ContextSync
 from schemas.fields.basic import PublicKey
 
 if TYPE_CHECKING:
diff --git a/beekeepy/beekeepy/_interface/asynchronous/beekeeper.py b/beekeepy/beekeepy/_interface/asynchronous/beekeeper.py
index 799922a0..d195568d 100644
--- a/beekeepy/beekeepy/_interface/asynchronous/beekeeper.py
+++ b/beekeepy/beekeepy/_interface/asynchronous/beekeeper.py
@@ -7,11 +7,11 @@ from loguru import logger
 from beekeepy._interface.abc.asynchronous.beekeeper import Beekeeper as BeekeeperInterface
 from beekeepy._interface.abc.packed_object import PackedAsyncBeekeeper
 from beekeepy._interface.asynchronous.session import Session
-from beekeepy._interface.delay_guard import AsyncDelayGuard
-from beekeepy._interface.state_invalidator import StateInvalidator
-from beekeepy._remote_handle.beekeeper import AsyncBeekeeper as AsynchronousRemoteBeekeeperHandle
-from beekeepy._runnable_handle.beekeeper import AsyncBeekeeper as AsynchronousBeekeeperHandle
-from beekeepy._runnable_handle.settings import Settings
+from beekeepy._interface.settings import InterfaceSettings
+from beekeepy._remote_handle import AsyncBeekeeperTemplate as AsynchronousRemoteBeekeeperHandle
+from beekeepy._runnable_handle import AsyncBeekeeperTemplate as AsynchronousBeekeeperHandle
+from beekeepy._utilities.delay_guard import AsyncDelayGuard
+from beekeepy._utilities.state_invalidator import StateInvalidator
 from beekeepy.exceptions import (
     DetachRemoteBeekeeperError,
     InvalidatedStateByClosingBeekeeperError,
@@ -19,15 +19,14 @@ from beekeepy.exceptions import (
 )
 
 if TYPE_CHECKING:
-    from beekeepy._communication.settings import CommunicationSettings
+    from beekeepy._communication import CommunicationSettings, HttpUrl
     from beekeepy._interface.abc.asynchronous.session import (
         Session as SessionInterface,
     )
-    from beekeepy._interface.url import HttpUrl
 
 
 class Beekeeper(BeekeeperInterface, StateInvalidator):
-    def __init__(self, *args: Any, handle: AsynchronousRemoteBeekeeperHandle, **kwargs: Any) -> None:
+    def __init__(self, *args: Any, handle: AsynchronousRemoteBeekeeperHandle[InterfaceSettings], **kwargs: Any) -> None:
         super().__init__(*args, **kwargs)
         self.__instance = handle
         self.__guard = AsyncDelayGuard()
@@ -48,7 +47,7 @@ class Beekeeper(BeekeeperInterface, StateInvalidator):
             self.__default_session = self.__create_session((await self._get_instance().session).token)
         return self.__default_session
 
-    def _get_instance(self) -> AsynchronousRemoteBeekeeperHandle:
+    def _get_instance(self) -> AsynchronousRemoteBeekeeperHandle[InterfaceSettings]:
         return self.__instance
 
     @StateInvalidator.empty_call_after_invalidation(None)
@@ -80,28 +79,38 @@ class Beekeeper(BeekeeperInterface, StateInvalidator):
         return PackedAsyncBeekeeper(settings=self.settings, unpack_factory=Beekeeper._remote_factory)
 
     @classmethod
-    async def _factory(cls, *, settings: Settings | None = None) -> BeekeeperInterface:
-        settings = settings or Settings()
-        handle = AsynchronousBeekeeperHandle(settings=settings, logger=logger)
+    async def _factory(cls, *, settings: InterfaceSettings | None = None) -> BeekeeperInterface:
+        settings = settings or InterfaceSettings()
+        handle = cls.__create_local_handle(settings=settings)
         handle.run()
         return cls(handle=handle)
 
     @classmethod
-    async def _remote_factory(cls, *, url_or_settings: Settings | HttpUrl) -> BeekeeperInterface:
-        if isinstance(url_or_settings, Settings):
+    async def _remote_factory(cls, *, url_or_settings: InterfaceSettings | HttpUrl) -> BeekeeperInterface:
+        if isinstance(url_or_settings, InterfaceSettings):
             assert (
                 url_or_settings.http_endpoint is not None
             ), "Settings.http_endpoint has to be set when passing to remote_factory"
-        settings = url_or_settings if isinstance(url_or_settings, Settings) else Settings(http_endpoint=url_or_settings)
+        settings = (
+            url_or_settings
+            if isinstance(url_or_settings, InterfaceSettings)
+            else InterfaceSettings(http_endpoint=url_or_settings)
+        )
         handle = AsynchronousRemoteBeekeeperHandle(settings=settings)
         cls.__apply_existing_session_token(settings=settings, handle=handle)
         return cls(handle=handle)
 
     @classmethod
-    def __apply_existing_session_token(cls, settings: Settings, handle: AsynchronousRemoteBeekeeperHandle) -> None:
+    def __apply_existing_session_token(
+        cls, settings: InterfaceSettings, handle: AsynchronousRemoteBeekeeperHandle[InterfaceSettings]
+    ) -> None:
         if settings.use_existing_session:
             handle.set_session_token(settings.use_existing_session)
 
+    @classmethod
+    def __create_local_handle(cls, settings: InterfaceSettings) -> AsynchronousBeekeeperHandle[InterfaceSettings]:
+        return AsynchronousBeekeeperHandle(settings=settings, logger=logger)
+
     async def _aenter(self) -> BeekeeperInterface:
         return self
 
diff --git a/beekeepy/beekeepy/_interface/asynchronous/session.py b/beekeepy/beekeepy/_interface/asynchronous/session.py
index 6e808935..470f58d0 100644
--- a/beekeepy/beekeepy/_interface/asynchronous/session.py
+++ b/beekeepy/beekeepy/_interface/asynchronous/session.py
@@ -9,8 +9,8 @@ from beekeepy._interface.asynchronous.wallet import (
     UnlockedWallet,
     Wallet,
 )
-from beekeepy._interface.state_invalidator import StateInvalidator
 from beekeepy._interface.validators import validate_digest, validate_public_keys, validate_timeout
+from beekeepy._utilities.state_invalidator import StateInvalidator
 from beekeepy.exceptions import (
     InvalidatedStateByClosingSessionError,
     InvalidWalletError,
@@ -27,8 +27,9 @@ if TYPE_CHECKING:
     from beekeepy._interface.abc.asynchronous.wallet import (
         Wallet as WalletInterface,
     )
-    from beekeepy._interface.delay_guard import AsyncDelayGuard
-    from beekeepy._remote_handle.beekeeper import AsyncBeekeeper as AsynchronousRemoteBeekeeperHandle
+    from beekeepy._interface.settings import InterfaceSettings
+    from beekeepy._remote_handle import AsyncBeekeeperTemplate as AsynchronousRemoteBeekeeperHandle
+    from beekeepy._utilities.delay_guard import AsyncDelayGuard
     from schemas.apis.beekeeper_api import GetInfo
     from schemas.fields.basic import PublicKey
     from schemas.fields.hex import Signature
@@ -38,7 +39,7 @@ class Session(SessionInterface, StateInvalidator):
     def __init__(
         self,
         *args: Any,
-        beekeeper: AsynchronousRemoteBeekeeperHandle,
+        beekeeper: AsynchronousRemoteBeekeeperHandle[InterfaceSettings],
         guard: AsyncDelayGuard,
         use_session_token: str | None = None,
         default_session_close_callback: Callable[[], None] | None = None,
diff --git a/beekeepy/beekeepy/_interface/asynchronous/wallet.py b/beekeepy/beekeepy/_interface/asynchronous/wallet.py
index 761cb085..20173c12 100644
--- a/beekeepy/beekeepy/_interface/asynchronous/wallet.py
+++ b/beekeepy/beekeepy/_interface/asynchronous/wallet.py
@@ -8,11 +8,12 @@ from beekeepy._interface.abc.asynchronous.wallet import (
 from beekeepy._interface.abc.asynchronous.wallet import (
     Wallet as WalletInterface,
 )
-from beekeepy._interface.delay_guard import AsyncDelayGuard
+from beekeepy._interface.settings import InterfaceSettings
 from beekeepy._interface.validators import validate_digest, validate_private_keys, validate_public_keys
 from beekeepy._interface.wallets_common import WalletCommons
-from beekeepy._remote_handle.beekeeper import AsyncBeekeeper as AsyncRemoteBeekeeper
-from beekeepy._runnable_handle.callbacks_protocol import AsyncWalletLocked
+from beekeepy._remote_handle import AsyncBeekeeperTemplate as AsyncRemoteBeekeeper
+from beekeepy._runnable_handle import AsyncWalletLocked
+from beekeepy._utilities.delay_guard import AsyncDelayGuard
 from beekeepy.exceptions import (
     InvalidPasswordError,
     InvalidPrivateKeyError,
@@ -29,7 +30,9 @@ if TYPE_CHECKING:
     from schemas.fields.hex import Signature
 
 
-class Wallet(WalletCommons[AsyncRemoteBeekeeper, AsyncWalletLocked, AsyncDelayGuard], WalletInterface):
+class Wallet(
+    WalletCommons[AsyncRemoteBeekeeper[InterfaceSettings], AsyncWalletLocked, AsyncDelayGuard], WalletInterface
+):
     @property
     async def public_keys(self) -> list[PublicKey]:
         return [
diff --git a/beekeepy/beekeepy/_interface/settings.py b/beekeepy/beekeepy/_interface/settings.py
new file mode 100644
index 00000000..701919db
--- /dev/null
+++ b/beekeepy/beekeepy/_interface/settings.py
@@ -0,0 +1,7 @@
+from __future__ import annotations
+
+from beekeepy._runnable_handle import RunnableHandleSettings
+
+
+class InterfaceSettings(RunnableHandleSettings):
+    """Settings for beekeeper interface."""
diff --git a/beekeepy/beekeepy/_interface/synchronous/beekeeper.py b/beekeepy/beekeepy/_interface/synchronous/beekeeper.py
index 3761fe38..e287b00b 100644
--- a/beekeepy/beekeepy/_interface/synchronous/beekeeper.py
+++ b/beekeepy/beekeepy/_interface/synchronous/beekeeper.py
@@ -6,12 +6,12 @@ from loguru import logger
 
 from beekeepy._interface.abc.packed_object import PackedSyncBeekeeper
 from beekeepy._interface.abc.synchronous.beekeeper import Beekeeper as BeekeeperInterface
-from beekeepy._interface.delay_guard import SyncDelayGuard
-from beekeepy._interface.state_invalidator import StateInvalidator
+from beekeepy._interface.settings import InterfaceSettings
 from beekeepy._interface.synchronous.session import Session
-from beekeepy._remote_handle.beekeeper import Beekeeper as SynchronousRemoteBeekeeperHandle
-from beekeepy._runnable_handle.beekeeper import Beekeeper as SynchronousBeekeeperHandle
-from beekeepy._runnable_handle.settings import Settings
+from beekeepy._remote_handle import BeekeeperTemplate as SynchronousRemoteBeekeeperHandle
+from beekeepy._runnable_handle import BeekeeperTemplate as SynchronousBeekeeperHandle
+from beekeepy._utilities.delay_guard import SyncDelayGuard
+from beekeepy._utilities.state_invalidator import StateInvalidator
 from beekeepy.exceptions import (
     DetachRemoteBeekeeperError,
     InvalidatedStateByClosingBeekeeperError,
@@ -19,15 +19,14 @@ from beekeepy.exceptions import (
 )
 
 if TYPE_CHECKING:
-    from beekeepy._communication.settings import CommunicationSettings
+    from beekeepy._communication import CommunicationSettings, HttpUrl
     from beekeepy._interface.abc.synchronous.session import (
         Session as SessionInterface,
     )
-    from beekeepy._interface.url import HttpUrl
 
 
 class Beekeeper(BeekeeperInterface, StateInvalidator):
-    def __init__(self, *args: Any, handle: SynchronousRemoteBeekeeperHandle, **kwargs: Any) -> None:
+    def __init__(self, *args: Any, handle: SynchronousRemoteBeekeeperHandle[InterfaceSettings], **kwargs: Any) -> None:
         super().__init__(*args, **kwargs)
         self.__instance = handle
         self.__guard = SyncDelayGuard()
@@ -48,7 +47,7 @@ class Beekeeper(BeekeeperInterface, StateInvalidator):
             self.__default_session = self.__create_session(self._get_instance().session.token, default_session=True)
         return self.__default_session
 
-    def _get_instance(self) -> SynchronousRemoteBeekeeperHandle:
+    def _get_instance(self) -> SynchronousRemoteBeekeeperHandle[InterfaceSettings]:
         return self.__instance
 
     @StateInvalidator.empty_call_after_invalidation(None)
@@ -80,28 +79,38 @@ class Beekeeper(BeekeeperInterface, StateInvalidator):
         return PackedSyncBeekeeper(settings=self.settings, unpack_factory=Beekeeper._remote_factory)
 
     @classmethod
-    def _factory(cls, *, settings: Settings | None = None) -> BeekeeperInterface:
-        settings = settings or Settings()
-        handle = SynchronousBeekeeperHandle(settings=settings, logger=logger)
+    def _factory(cls, *, settings: InterfaceSettings | None = None) -> BeekeeperInterface:
+        settings = settings or InterfaceSettings()
+        handle = cls.__create_local_handle(settings=settings)
         handle.run()
         return cls(handle=handle)
 
     @classmethod
-    def _remote_factory(cls, *, url_or_settings: Settings | HttpUrl) -> BeekeeperInterface:
-        if isinstance(url_or_settings, Settings):
+    def _remote_factory(cls, *, url_or_settings: InterfaceSettings | HttpUrl) -> BeekeeperInterface:
+        if isinstance(url_or_settings, InterfaceSettings):
             assert (
                 url_or_settings.http_endpoint is not None
             ), "Settings.http_endpoint has to be set when passing to remote_factory"
-        settings = url_or_settings if isinstance(url_or_settings, Settings) else Settings(http_endpoint=url_or_settings)
+        settings = (
+            url_or_settings
+            if isinstance(url_or_settings, InterfaceSettings)
+            else InterfaceSettings(http_endpoint=url_or_settings)
+        )
         handle = SynchronousRemoteBeekeeperHandle(settings=settings)
         cls.__apply_existing_session_token(settings=settings, handle=handle)
         return cls(handle=handle)
 
     @classmethod
-    def __apply_existing_session_token(cls, settings: Settings, handle: SynchronousRemoteBeekeeperHandle) -> None:
+    def __apply_existing_session_token(
+        cls, settings: InterfaceSettings, handle: SynchronousRemoteBeekeeperHandle[InterfaceSettings]
+    ) -> None:
         if settings.use_existing_session:
             handle.set_session_token(settings.use_existing_session)
 
+    @classmethod
+    def __create_local_handle(cls, settings: InterfaceSettings) -> SynchronousBeekeeperHandle[InterfaceSettings]:
+        return SynchronousBeekeeperHandle(settings=settings, logger=logger)
+
     def _enter(self) -> BeekeeperInterface:
         return self
 
diff --git a/beekeepy/beekeepy/_interface/synchronous/session.py b/beekeepy/beekeepy/_interface/synchronous/session.py
index 3e616a05..5b9e559d 100644
--- a/beekeepy/beekeepy/_interface/synchronous/session.py
+++ b/beekeepy/beekeepy/_interface/synchronous/session.py
@@ -4,12 +4,12 @@ from typing import TYPE_CHECKING, Any, Callable
 
 from beekeepy._interface.abc.synchronous.session import Password
 from beekeepy._interface.abc.synchronous.session import Session as SessionInterface
-from beekeepy._interface.state_invalidator import StateInvalidator
 from beekeepy._interface.synchronous.wallet import (
     UnlockedWallet,
     Wallet,
 )
 from beekeepy._interface.validators import validate_digest, validate_public_keys, validate_timeout
+from beekeepy._utilities.state_invalidator import StateInvalidator
 from beekeepy.exceptions import (
     InvalidatedStateByClosingSessionError,
     InvalidWalletError,
@@ -26,8 +26,9 @@ if TYPE_CHECKING:
     from beekeepy._interface.abc.synchronous.wallet import (
         Wallet as WalletInterface,
     )
-    from beekeepy._interface.delay_guard import SyncDelayGuard
-    from beekeepy._remote_handle.beekeeper import Beekeeper as SyncRemoteBeekeeper
+    from beekeepy._interface.settings import InterfaceSettings
+    from beekeepy._remote_handle import BeekeeperTemplate as SyncRemoteBeekeeper
+    from beekeepy._utilities.delay_guard import SyncDelayGuard
     from schemas.apis.beekeeper_api import GetInfo
     from schemas.fields.basic import PublicKey
     from schemas.fields.hex import Signature
@@ -37,7 +38,7 @@ class Session(SessionInterface, StateInvalidator):
     def __init__(
         self,
         *args: Any,
-        beekeeper: SyncRemoteBeekeeper,
+        beekeeper: SyncRemoteBeekeeper[InterfaceSettings],
         guard: SyncDelayGuard,
         use_session_token: str | None = None,
         default_session_close_callback: Callable[[], None] | None = None,
diff --git a/beekeepy/beekeepy/_interface/synchronous/wallet.py b/beekeepy/beekeepy/_interface/synchronous/wallet.py
index d23ae316..81ffec89 100644
--- a/beekeepy/beekeepy/_interface/synchronous/wallet.py
+++ b/beekeepy/beekeepy/_interface/synchronous/wallet.py
@@ -8,11 +8,12 @@ from beekeepy._interface.abc.synchronous.wallet import (
 from beekeepy._interface.abc.synchronous.wallet import (
     Wallet as WalletInterface,
 )
-from beekeepy._interface.delay_guard import SyncDelayGuard
+from beekeepy._interface.settings import InterfaceSettings
 from beekeepy._interface.validators import validate_private_keys, validate_public_keys
 from beekeepy._interface.wallets_common import WalletCommons
-from beekeepy._remote_handle.beekeeper import Beekeeper as SyncRemoteBeekeeper
-from beekeepy._runnable_handle.callbacks_protocol import SyncWalletLocked
+from beekeepy._remote_handle import BeekeeperTemplate as SyncRemoteBeekeeper
+from beekeepy._runnable_handle import SyncWalletLocked
+from beekeepy._utilities.delay_guard import SyncDelayGuard
 from beekeepy.exceptions import (
     InvalidPasswordError,
     InvalidPrivateKeyError,
@@ -29,7 +30,7 @@ if TYPE_CHECKING:
     from schemas.fields.hex import Signature
 
 
-class Wallet(WalletCommons[SyncRemoteBeekeeper, SyncWalletLocked, SyncDelayGuard], WalletInterface):
+class Wallet(WalletCommons[SyncRemoteBeekeeper[InterfaceSettings], SyncWalletLocked, SyncDelayGuard], WalletInterface):
     @property
     def public_keys(self) -> list[PublicKey]:
         return [
diff --git a/beekeepy/beekeepy/_interface/wallets_common.py b/beekeepy/beekeepy/_interface/wallets_common.py
index 2750323c..328655f4 100644
--- a/beekeepy/beekeepy/_interface/wallets_common.py
+++ b/beekeepy/beekeepy/_interface/wallets_common.py
@@ -6,11 +6,12 @@ from asyncio import iscoroutinefunction
 from functools import wraps
 from typing import TYPE_CHECKING, Any, Generic, NoReturn, ParamSpec, TypeVar, overload
 
-from beekeepy._interface.delay_guard import AsyncDelayGuard, SyncDelayGuard
-from beekeepy._interface.state_invalidator import StateInvalidator
-from beekeepy._remote_handle.beekeeper import AsyncBeekeeper as AsyncRemoteBeekeeper
-from beekeepy._remote_handle.beekeeper import Beekeeper as SyncRemoteBeekeeper
-from beekeepy._runnable_handle.callbacks_protocol import AsyncWalletLocked, SyncWalletLocked
+from beekeepy._interface.settings import InterfaceSettings
+from beekeepy._remote_handle import AsyncBeekeeperTemplate as AsyncRemoteBeekeeper
+from beekeepy._remote_handle import BeekeeperTemplate as SyncRemoteBeekeeper
+from beekeepy._runnable_handle import AsyncWalletLocked, SyncWalletLocked
+from beekeepy._utilities.delay_guard import AsyncDelayGuard, SyncDelayGuard
+from beekeepy._utilities.state_invalidator import StateInvalidator
 from beekeepy.exceptions import WalletIsLockedError
 
 if TYPE_CHECKING:
@@ -20,7 +21,9 @@ if TYPE_CHECKING:
 
 P = ParamSpec("P")
 ResultT = TypeVar("ResultT")
-BeekeeperT = TypeVar("BeekeeperT", bound=SyncRemoteBeekeeper | AsyncRemoteBeekeeper)
+BeekeeperT = TypeVar(
+    "BeekeeperT", bound=SyncRemoteBeekeeper[InterfaceSettings] | AsyncRemoteBeekeeper[InterfaceSettings]
+)
 CallbackT = TypeVar("CallbackT", bound=AsyncWalletLocked | SyncWalletLocked)
 GuardT = TypeVar("GuardT", bound=SyncDelayGuard | AsyncDelayGuard)
 
@@ -132,7 +135,9 @@ class WalletCommons(ContainsWalletName, StateInvalidator, Generic[BeekeeperT, Ca
 
             @wraps(wrapped_function)
             async def async_impl(*args: P.args, **kwrags: P.kwargs) -> ResultT:
-                this: WalletCommons[AsyncRemoteBeekeeper, AsyncWalletLocked, AsyncDelayGuard] = args[0]  # type: ignore[assignment]
+                this: WalletCommons[AsyncRemoteBeekeeper[InterfaceSettings], AsyncWalletLocked, AsyncDelayGuard] = args[
+                    0
+                ]  # type: ignore[assignment]
                 await this._async_call_callback_if_locked(wallet_name=this.name, token=this.session_token)
                 return await wrapped_function(*args, **kwrags)  # type: ignore[no-any-return]
 
@@ -140,7 +145,7 @@ class WalletCommons(ContainsWalletName, StateInvalidator, Generic[BeekeeperT, Ca
 
         @wraps(wrapped_function)
         def sync_impl(*args: P.args, **kwrags: P.kwargs) -> ResultT:
-            this: WalletCommons[SyncRemoteBeekeeper, SyncWalletLocked, SyncDelayGuard] = args[0]  # type: ignore[assignment]
+            this: WalletCommons[SyncRemoteBeekeeper[InterfaceSettings], SyncWalletLocked, SyncDelayGuard] = args[0]  # type: ignore[assignment]
             this._sync_call_callback_if_locked(wallet_name=this.name, token=this.session_token)
             return wrapped_function(*args, **kwrags)  # type: ignore[return-value]
 
diff --git a/beekeepy/beekeepy/_remote_handle/__init__.py b/beekeepy/beekeepy/_remote_handle/__init__.py
index e69de29b..e8d89f4d 100644
--- a/beekeepy/beekeepy/_remote_handle/__init__.py
+++ b/beekeepy/beekeepy/_remote_handle/__init__.py
@@ -0,0 +1,30 @@
+from __future__ import annotations
+
+from beekeepy._remote_handle.abc.batch_handle import ApiFactory, AsyncBatchHandle, SyncBatchHandle
+from beekeepy._remote_handle.abc.handle import AbstractAsyncHandle, AbstractSyncHandle
+from beekeepy._remote_handle.app_status_probe import AppStatusProbe
+from beekeepy._remote_handle.beekeeper import AsyncBeekeeper as AsyncBeekeeperTemplate
+from beekeepy._remote_handle.beekeeper import Beekeeper as BeekeeperTemplate
+from beekeepy._remote_handle.beekeeper import _AsyncSessionBatchHandle as AsyncBeekeeprBatchHandle
+from beekeepy._remote_handle.beekeeper import _SyncSessionBatchHandle as SyncBeekeeprBatchHandle
+from beekeepy._remote_handle.settings import RemoteHandleSettings
+
+AsyncBeekeeper = AsyncBeekeeperTemplate[RemoteHandleSettings]
+Beekeeper = BeekeeperTemplate[RemoteHandleSettings]
+
+__all__ = [
+    "AbstractAsyncHandle",
+    "AbstractSyncHandle",
+    "ApiFactory",
+    "AppStatusProbe",
+    "AsyncBatchHandle",
+    "AsyncBeekeeper",
+    "AsyncBeekeeperTemplate",
+    "AsyncBeekeeprBatchHandle",
+    "Beekeeper",
+    "BeekeeperTemplate",
+    "RemoteHandleSettings",
+    "SyncBatchHandle",
+    "SyncBatchHandle",
+    "SyncBeekeeprBatchHandle",
+]
diff --git a/beekeepy/beekeepy/_remote_handle/batch_handle.py b/beekeepy/beekeepy/_remote_handle/abc/batch_handle.py
similarity index 94%
rename from beekeepy/beekeepy/_remote_handle/batch_handle.py
rename to beekeepy/beekeepy/_remote_handle/abc/batch_handle.py
index a8e364f4..e0ad80a1 100644
--- a/beekeepy/beekeepy/_remote_handle/batch_handle.py
+++ b/beekeepy/beekeepy/_remote_handle/abc/batch_handle.py
@@ -6,8 +6,9 @@ from dataclasses import dataclass
 from typing import TYPE_CHECKING, Any, Generic, TypeVar
 
 from beekeepy import exceptions
-from beekeepy._interface.context import ContextAsync, ContextSync, EnterReturnT
-from beekeepy._remote_handle.build_json_rpc_call import build_json_rpc_call
+from beekeepy._apis.abc import AsyncSendable, SyncSendable
+from beekeepy._utilities.build_json_rpc_call import build_json_rpc_call
+from beekeepy._utilities.context import ContextAsync, ContextSync, EnterReturnT
 from schemas.jsonrpc import ExpectResultT, JSONRPCResult, get_response_model
 
 if TYPE_CHECKING:
@@ -15,8 +16,7 @@ if TYPE_CHECKING:
 
     from typing_extensions import Self
 
-    from beekeepy._communication.abc.overseer import AbstractOverseer
-    from beekeepy._interface.url import HttpUrl
+    from beekeepy._communication import AbstractOverseer, HttpUrl
 
 
 class _DelayedResponseWrapper:
@@ -207,7 +207,7 @@ OwnerT = TypeVar("OwnerT")
 ApiFactory = Callable[[OwnerT], ApiT]
 
 
-class SyncBatchHandle(_BatchHandle["SyncBatchHandle"], Generic[ApiT]):  # type: ignore[type-arg]
+class SyncBatchHandle(_BatchHandle["SyncBatchHandle"], SyncSendable, Generic[ApiT]):  # type: ignore[type-arg]
     def __init__(
         self,
         url: HttpUrl,
@@ -224,7 +224,7 @@ class SyncBatchHandle(_BatchHandle["SyncBatchHandle"], Generic[ApiT]):  # type:
         return self._impl_handle_request(endpoint, params, expect_type=expected_type)  # type: ignore[arg-type]
 
 
-class AsyncBatchHandle(_BatchHandle["AsyncBatchHandle"], Generic[ApiT]):  # type: ignore[type-arg]
+class AsyncBatchHandle(_BatchHandle["AsyncBatchHandle"], AsyncSendable, Generic[ApiT]):  # type: ignore[type-arg]
     def __init__(
         self,
         url: HttpUrl,
diff --git a/beekeepy/beekeepy/_remote_handle/abc/handle.py b/beekeepy/beekeepy/_remote_handle/abc/handle.py
index dc77ea88..3005e4f1 100644
--- a/beekeepy/beekeepy/_remote_handle/abc/handle.py
+++ b/beekeepy/beekeepy/_remote_handle/abc/handle.py
@@ -5,36 +5,40 @@ from typing import TYPE_CHECKING, Any, Generic, TypeVar
 
 from loguru import logger as loguru_logger
 
-from beekeepy._communication.aiohttp_communicator import AioHttpCommunicator
-from beekeepy._communication.request_communicator import RequestCommunicator
-from beekeepy._interface.context import SelfContextAsync, SelfContextSync
-from beekeepy._interface.settings_holder import UniqueSettingsHolder
-from beekeepy._interface.stopwatch import Stopwatch
-from beekeepy._remote_handle.build_json_rpc_call import build_json_rpc_call
-from beekeepy._remote_handle.settings import Settings
+from beekeepy._apis.abc import (
+    AbstractAsyncApiCollection,
+    AbstractSyncApiCollection,
+)
+from beekeepy._apis.abc.sendable import AsyncSendable, SyncSendable
+from beekeepy._communication import AioHttpCommunicator, RequestCommunicator
+from beekeepy._remote_handle.settings import RemoteHandleSettings
+from beekeepy._utilities.build_json_rpc_call import build_json_rpc_call
+from beekeepy._utilities.context import SelfContextAsync, SelfContextSync
+from beekeepy._utilities.settings_holder import UniqueSettingsHolder
+from beekeepy._utilities.stopwatch import Stopwatch
 from beekeepy.exceptions import CommunicationError
 from schemas.jsonrpc import ExpectResultT, JSONRPCResult, get_response_model
 
 if TYPE_CHECKING:
     from loguru import Logger
 
-    from beekeepy._communication.abc.communicator import AbstractCommunicator
-    from beekeepy._communication.abc.overseer import AbstractOverseer
-    from beekeepy._interface.url import HttpUrl
-    from beekeepy._remote_handle.batch_handle import AsyncBatchHandle, SyncBatchHandle
+    from beekeepy._communication import AbstractCommunicator, AbstractOverseer, HttpUrl
+    from beekeepy._remote_handle.abc.batch_handle import AsyncBatchHandle, SyncBatchHandle
     from beekeepy.exceptions import Json
 
+RemoteSettingsT = TypeVar("RemoteSettingsT", bound=RemoteHandleSettings)
 
-ApiT = TypeVar("ApiT")
 
+ApiT = TypeVar("ApiT", bound=AbstractAsyncApiCollection | AbstractSyncApiCollection)
 
-class AbstractHandle(UniqueSettingsHolder[Settings], ABC, Generic[ApiT]):
+
+class AbstractHandle(UniqueSettingsHolder[RemoteSettingsT], ABC, Generic[RemoteSettingsT, ApiT]):
     """Provides basic interface for all network handles."""
 
     def __init__(
         self,
         *args: Any,
-        settings: Settings,
+        settings: RemoteSettingsT,
         logger: Logger | None = None,
         **kwargs: Any,
     ) -> None:
@@ -63,6 +67,10 @@ class AbstractHandle(UniqueSettingsHolder[Settings], ABC, Generic[ApiT]):
         with self.update_settings() as settings:
             settings.http_endpoint = value
 
+    @property
+    def apis(self) -> ApiT:
+        return self.__api
+
     @property
     def api(self) -> ApiT:
         return self.__api
@@ -138,14 +146,14 @@ class AbstractHandle(UniqueSettingsHolder[Settings], ABC, Generic[ApiT]):
         )
 
 
-class AbstractAsyncHandle(AbstractHandle[ApiT], SelfContextAsync, ABC):
+class AbstractAsyncHandle(AbstractHandle[RemoteSettingsT, ApiT], SelfContextAsync, AsyncSendable, ABC):
     """Base class for service handlers that uses asynchronous communication."""
 
     async def _async_send(
         self, *, endpoint: str, params: str, expected_type: type[ExpectResultT]
     ) -> JSONRPCResult[ExpectResultT]:
         """Sends data asynchronously to handled service basing on jsonrpc."""
-        from beekeepy._interface.error_logger import ErrorLogger
+        from beekeepy._utilities.error_logger import ErrorLogger
 
         request = build_json_rpc_call(method=endpoint, params=params)
         self._log_request(request)
@@ -168,12 +176,12 @@ class AbstractAsyncHandle(AbstractHandle[ApiT], SelfContextAsync, ABC):
         self.teardown()
 
 
-class AbstractSyncHandle(AbstractHandle[ApiT], SelfContextSync, ABC):
+class AbstractSyncHandle(AbstractHandle[RemoteSettingsT, ApiT], SelfContextSync, SyncSendable, ABC):
     """Base class for service handlers that uses synchronous communication."""
 
     def _send(self, *, endpoint: str, params: str, expected_type: type[ExpectResultT]) -> JSONRPCResult[ExpectResultT]:
         """Sends data synchronously to handled service basing on jsonrpc."""
-        from beekeepy._interface.error_logger import ErrorLogger
+        from beekeepy._utilities.error_logger import ErrorLogger
 
         request = build_json_rpc_call(method=endpoint, params=params)
         self._log_request(request)
diff --git a/beekeepy/beekeepy/_remote_handle/api/__init__.py b/beekeepy/beekeepy/_remote_handle/api/__init__.py
deleted file mode 100644
index 6d4be492..00000000
--- a/beekeepy/beekeepy/_remote_handle/api/__init__.py
+++ /dev/null
@@ -1,6 +0,0 @@
-from __future__ import annotations
-
-from beekeepy._remote_handle.api.async_api import BeekeeperApi as AsyncBeekeeperApi
-from beekeepy._remote_handle.api.sync_api import BeekeeperApi as SyncBeekeeperApi
-
-__all__ = ["AsyncBeekeeperApi", "SyncBeekeeperApi"]
diff --git a/beekeepy/beekeepy/_remote_handle/api/api_collection.py b/beekeepy/beekeepy/_remote_handle/api/api_collection.py
deleted file mode 100644
index 37b2a0ff..00000000
--- a/beekeepy/beekeepy/_remote_handle/api/api_collection.py
+++ /dev/null
@@ -1,39 +0,0 @@
-from __future__ import annotations
-
-from typing import TYPE_CHECKING
-
-from beekeepy._remote_handle.abc.api_collection import (
-    AbstractAsyncApiCollection,
-    AbstractSyncApiCollection,
-)
-from beekeepy._remote_handle.api import AsyncBeekeeperApi, SyncBeekeeperApi
-
-if TYPE_CHECKING:
-    from beekeepy._remote_handle.beekeeper import (
-        AsyncBeekeeper,
-        Beekeeper,
-        _AsyncSessionBatchHandle,
-        _SyncSessionBatchHandle,
-    )
-
-
-class BeekeeperAsyncApiCollection(AbstractAsyncApiCollection):
-    """Beekeepers collection of available apis in async version."""
-
-    _owner: AsyncBeekeeper | _AsyncSessionBatchHandle
-
-    def __init__(self, owner: AsyncBeekeeper | _AsyncSessionBatchHandle) -> None:
-        super().__init__(owner)
-        self.beekeeper = AsyncBeekeeperApi(owner=self._owner)
-        self.beekeeper_api = self.beekeeper
-
-
-class BeekeeperSyncApiCollection(AbstractSyncApiCollection):
-    """Beekeepers collection of available apis in async version."""
-
-    _owner: Beekeeper | _SyncSessionBatchHandle
-
-    def __init__(self, owner: Beekeeper | _SyncSessionBatchHandle) -> None:
-        super().__init__(owner)
-        self.beekeeper = SyncBeekeeperApi(owner=self._owner)
-        self.beekeeper_api = self.beekeeper
diff --git a/beekeepy/beekeepy/_remote_handle/app_status_probe.py b/beekeepy/beekeepy/_remote_handle/app_status_probe.py
new file mode 100644
index 00000000..432901fb
--- /dev/null
+++ b/beekeepy/beekeepy/_remote_handle/app_status_probe.py
@@ -0,0 +1,28 @@
+from __future__ import annotations
+
+from typing import TYPE_CHECKING
+
+from beekeepy._apis import AppStatusProbeSyncApiCollection
+from beekeepy._remote_handle.abc.handle import AbstractSyncHandle
+from beekeepy._remote_handle.settings import RemoteHandleSettings
+
+if TYPE_CHECKING:
+    from beekeepy._apis import SyncAppStatusApi
+    from beekeepy._remote_handle.abc.batch_handle import SyncBatchHandle
+
+
+class AppStatusProbe(AbstractSyncHandle[RemoteHandleSettings, AppStatusProbeSyncApiCollection]):
+    """Synchronous handle for probing."""
+
+    def _construct_api(self) -> AppStatusProbeSyncApiCollection:
+        return AppStatusProbeSyncApiCollection(owner=self)
+
+    @property
+    def api(self) -> SyncAppStatusApi:  # type: ignore[override]
+        return self.apis.app_status
+
+    def _target_service(self) -> str:
+        return "app_status_probe"
+
+    def batch(self, *, delay_error_on_data_access: bool = False) -> SyncBatchHandle[AppStatusProbeSyncApiCollection]:
+        raise NotImplementedError
diff --git a/beekeepy/beekeepy/_remote_handle/beekeeper.py b/beekeepy/beekeepy/_remote_handle/beekeeper.py
index 4d077cde..62483d26 100644
--- a/beekeepy/beekeepy/_remote_handle/beekeeper.py
+++ b/beekeepy/beekeepy/_remote_handle/beekeeper.py
@@ -3,21 +3,24 @@ from __future__ import annotations
 import uuid
 from typing import TYPE_CHECKING, Any, NoReturn
 
-from beekeepy._interface._sanitize import sanitize
-from beekeepy._remote_handle.abc.handle import AbstractAsyncHandle, AbstractSyncHandle
-from beekeepy._remote_handle.api.api_collection import (
+from beekeepy._apis import (
+    AsyncBeekeeperApi,
     BeekeeperAsyncApiCollection,
     BeekeeperSyncApiCollection,
+    SyncBeekeeperApi,
 )
-from beekeepy._remote_handle.api.session_holder import AsyncSessionHolder, SyncSessionHolder
-from beekeepy._remote_handle.batch_handle import ApiFactory, AsyncBatchHandle, SyncBatchHandle
+from beekeepy._apis.abc import (
+    AsyncSessionHolder,
+    SyncSessionHolder,
+)
+from beekeepy._remote_handle.abc.batch_handle import ApiFactory, AsyncBatchHandle, SyncBatchHandle
+from beekeepy._remote_handle.abc.handle import AbstractAsyncHandle, AbstractSyncHandle, RemoteSettingsT
+from beekeepy._utilities.sanitize import sanitize
 
 if TYPE_CHECKING:
     from typing_extensions import Self
 
-    from beekeepy._communication.abc.overseer import AbstractOverseer
-    from beekeepy._interface.url import HttpUrl
-    from beekeepy._remote_handle.api import AsyncBeekeeperApi, SyncBeekeeperApi
+    from beekeepy._communication import AbstractOverseer, HttpUrl
     from beekeepy.exceptions import Json
 
 _handle_target_service_name = "beekeeper"
@@ -67,15 +70,19 @@ class _AsyncSessionBatchHandle(AsyncBatchHandle[BeekeeperAsyncApiCollection], As
         _raise_acquire_not_possible()
 
 
-class Beekeeper(AbstractSyncHandle[BeekeeperSyncApiCollection], SyncSessionHolder):
+class Beekeeper(AbstractSyncHandle[RemoteSettingsT, BeekeeperSyncApiCollection], SyncSessionHolder):
     """Synchronous handle for beekeeper service communication."""
 
     def _construct_api(self) -> BeekeeperSyncApiCollection:
         return BeekeeperSyncApiCollection(owner=self)
 
+    @property
+    def apis(self) -> BeekeeperSyncApiCollection:
+        return super().api
+
     @property
     def api(self) -> SyncBeekeeperApi:  # type: ignore[override]
-        return super().api.beekeeper
+        return self.apis.beekeeper
 
     def _target_service(self) -> str:
         return _handle_target_service_name
@@ -99,15 +106,19 @@ class Beekeeper(AbstractSyncHandle[BeekeeperSyncApiCollection], SyncSessionHolde
         return sanitize(data)
 
 
-class AsyncBeekeeper(AbstractAsyncHandle[BeekeeperAsyncApiCollection], AsyncSessionHolder):
+class AsyncBeekeeper(AbstractAsyncHandle[RemoteSettingsT, BeekeeperAsyncApiCollection], AsyncSessionHolder):
     """Asynchronous handle for beekeeper service communication."""
 
     def _construct_api(self) -> BeekeeperAsyncApiCollection:
         return BeekeeperAsyncApiCollection(owner=self)
 
+    @property
+    def apis(self) -> BeekeeperAsyncApiCollection:
+        return super().api
+
     @property
     def api(self) -> AsyncBeekeeperApi:  # type: ignore[override]
-        return super().api.beekeeper
+        return self.apis.beekeeper
 
     def _target_service(self) -> str:
         return _handle_target_service_name
diff --git a/beekeepy/beekeepy/_remote_handle/settings.py b/beekeepy/beekeepy/_remote_handle/settings.py
index ede77a6a..470e6c1a 100644
--- a/beekeepy/beekeepy/_remote_handle/settings.py
+++ b/beekeepy/beekeepy/_remote_handle/settings.py
@@ -2,14 +2,16 @@ from __future__ import annotations
 
 from typing import ClassVar
 
-from beekeepy._communication.abc.communicator import AbstractCommunicator  # noqa: TCH001
-from beekeepy._communication.abc.overseer import AbstractOverseer
-from beekeepy._communication.overseers import CommonOverseer
-from beekeepy._communication.settings import CommunicationSettings
-from beekeepy._interface.url import HttpUrl  # noqa: TCH001
+from beekeepy._communication import (
+    AbstractCommunicator,
+    AbstractOverseer,
+    CommonOverseer,
+    CommunicationSettings,
+    HttpUrl,
+)
 
 
-class Settings(CommunicationSettings):
+class RemoteHandleSettings(CommunicationSettings):
     class Defaults(CommunicationSettings.Defaults):
         OVERSEER: ClassVar[type[AbstractOverseer]] = CommonOverseer
 
diff --git a/beekeepy/beekeepy/_runnable_handle/__init__.py b/beekeepy/beekeepy/_runnable_handle/__init__.py
index 3e4206d5..65e1c9a6 100644
--- a/beekeepy/beekeepy/_runnable_handle/__init__.py
+++ b/beekeepy/beekeepy/_runnable_handle/__init__.py
@@ -1,6 +1,21 @@
 from __future__ import annotations
 
-from beekeepy._runnable_handle.beekeeper import AsyncBeekeeper, Beekeeper
-from beekeepy._runnable_handle.notification_handler_base import BeekeeperNotificationHandler
+from beekeepy._runnable_handle.beekeeper import AsyncBeekeeper as AsyncBeekeeperTemplate
+from beekeepy._runnable_handle.beekeeper import Beekeeper as BeekeeperTemplate
+from beekeepy._runnable_handle.callbacks_protocol import AsyncWalletLocked, SyncWalletLocked
+from beekeepy._runnable_handle.close_already_running_beekeeper import close_already_running_beekeeper
+from beekeepy._runnable_handle.settings import Settings as RunnableHandleSettings
 
-__all__ = ["AsyncBeekeeper", "Beekeeper", "BeekeeperNotificationHandler"]
+AsyncBeekeeper = AsyncBeekeeperTemplate[RunnableHandleSettings]
+Beekeeper = BeekeeperTemplate[RunnableHandleSettings]
+
+__all__ = [
+    "AsyncBeekeeper",
+    "AsyncBeekeeperTemplate",
+    "AsyncWalletLocked",
+    "Beekeeper",
+    "BeekeeperTemplate",
+    "close_already_running_beekeeper",
+    "RunnableHandleSettings",
+    "SyncWalletLocked",
+]
diff --git a/beekeepy/beekeepy/_runnable_handle/beekeeper.py b/beekeepy/beekeepy/_runnable_handle/beekeeper.py
index 58bc9f66..912ca3df 100644
--- a/beekeepy/beekeepy/_runnable_handle/beekeeper.py
+++ b/beekeepy/beekeepy/_runnable_handle/beekeeper.py
@@ -1,250 +1,114 @@
 from __future__ import annotations
 
-from abc import ABC, abstractmethod
-from subprocess import CalledProcessError
-from typing import TYPE_CHECKING, Any, TypeVar, cast
-
-from beekeepy._communication.universal_notification_server import (
-    UniversalNotificationServer,
-)
-from beekeepy._executable import BeekeeperArguments, BeekeeperExecutable
-from beekeepy._executable.arguments.beekeeper_arguments import (
-    BeekeeperArgumentsDefaults,
-)
-from beekeepy._interface.url import HttpUrl
-from beekeepy._remote_handle import beekeeper as remote_beekeeper
-from beekeepy._runnable_handle.beekeeper_callbacks import BeekeeperNotificationCallbacks
-from beekeepy._runnable_handle.beekeeper_notification_handler import NotificationHandler
+from typing import TYPE_CHECKING, TypeVar
+
+from beekeepy._executable import BeekeeperArguments, BeekeeperConfig, BeekeeperExecutable
+from beekeepy._remote_handle import AsyncBeekeeperTemplate, BeekeeperTemplate
+from beekeepy._runnable_handle.runnable_handle import RunnableHandle
 from beekeepy._runnable_handle.settings import Settings
-from beekeepy.exceptions import (
-    BeekeeperFailedToStartDuringProcessSpawnError,
-    BeekeeperFailedToStartNotReadyOnTimeError,
-    BeekeeperIsNotRunningError,
-)
+from beekeepy.exceptions import BeekeeperFailedToStartError, ExecutableError
 
 if TYPE_CHECKING:
     from pathlib import Path
 
-    from loguru import Logger
-
-    from beekeepy._executable.beekeeper_config import BeekeeperConfig
-    from beekeepy._interface.key_pair import KeyPair
-    from schemas.notifications import (
-        Error,
-        Notification,
-        Status,
-        WebserverListening,
-    )
-
-
-EnterReturnT = TypeVar("EnterReturnT", bound=remote_beekeeper.Beekeeper | remote_beekeeper.AsyncBeekeeper)
+    from beekeepy._communication import HttpUrl
+    from beekeepy._executable import KeyPair
+    from beekeepy._runnable_handle.match_ports import PortMatchingResult
 
 
 __all__ = [
-    "SyncRemoteBeekeeper",
-    "AsyncRemoteBeekeeper",
     "Beekeeper",
     "AsyncBeekeeper",
 ]
 
+RunnableSettingsT = TypeVar("RunnableSettingsT", bound=Settings)
 
-class SyncRemoteBeekeeper(remote_beekeeper.Beekeeper):
-    pass
-
-
-class AsyncRemoteBeekeeper(remote_beekeeper.AsyncBeekeeper):
-    pass
-
-
-class BeekeeperCommon(BeekeeperNotificationCallbacks, ABC):
-    def __init__(self, *args: Any, settings: Settings, logger: Logger, **kwargs: Any) -> None:
-        super().__init__(*args, settings=settings, logger=logger, **kwargs)
-        self.__exec = BeekeeperExecutable(settings, logger)
-        self.__notification_server: UniversalNotificationServer | None = None
-        self.__notification_event_handler: NotificationHandler | None = None
-        self.__logger = logger
 
-    @property
-    def pid(self) -> int:
-        if not self.is_running:
-            raise BeekeeperIsNotRunningError
-        return self.__exec.pid
+class RunnableBeekeeper(RunnableHandle[BeekeeperExecutable, BeekeeperConfig, BeekeeperArguments, Settings]):
+    def _construct_executable(self) -> BeekeeperExecutable:
+        settings = self._get_settings()
+        return BeekeeperExecutable(
+            executable_path=settings.binary_path,
+            working_directory=settings.ensured_working_directory,
+            logger=self._logger,
+        )
 
-    @property
-    def notification_endpoint(self) -> HttpUrl:
-        endpoint = self._get_settings().notification_endpoint
-        assert endpoint is not None, "Notification endpoint is not set"
-        return endpoint
+    def _get_working_directory_from_cli_arguments(self) -> Path | None:
+        return self.arguments.data_dir
 
-    @property
-    def config(self) -> BeekeeperConfig:
-        return self.__exec.config
+    def _get_http_endpoint_from_cli_arguments(self) -> HttpUrl | None:
+        return self.arguments.webserver_http_endpoint
 
-    @property
-    def is_running(self) -> bool:
-        return self.__exec is not None and self.__exec.is_running()
+    def _get_http_endpoint_from_config(self) -> HttpUrl | None:
+        return self.config.webserver_http_endpoint
 
-    def __setup_notification_server(self, *, address_from_cli_arguments: HttpUrl | None = None) -> None:
-        assert self.__notification_server is None, "Notification server already exists, previous hasn't been close?"
-        assert (
-            self.__notification_event_handler is None
-        ), "Notification event handler already exists, previous hasn't been close?"
+    def _unify_cli_arguments(self, working_directory: Path, http_endpoint: HttpUrl) -> None:
+        self.arguments.data_dir = working_directory
+        self.arguments.webserver_http_endpoint = http_endpoint
 
-        self.__notification_event_handler = NotificationHandler(self)
-        self.__notification_server = UniversalNotificationServer(
-            self.__notification_event_handler,
-            notification_endpoint=address_from_cli_arguments
-            or self._get_settings().notification_endpoint,  # this has to be accessed directly from settings
-        )
+    def _unify_config(self, working_directory: Path, http_endpoint: HttpUrl) -> None:  # noqa: ARG002
+        self.config.webserver_http_endpoint = http_endpoint
 
-    def __close_notification_server(self) -> None:
-        if self.__notification_server is not None:
-            self.__notification_server.close()
-            self.__notification_server = None
-
-        if self.__notification_event_handler is not None:
-            self.__notification_event_handler = None
-
-    def __wait_till_ready(self) -> None:
-        assert self.__notification_event_handler is not None, "Notification event handler hasn't been set"
-        if not self.__notification_event_handler.http_listening_event.wait(
-            timeout=self._get_settings().initialization_timeout.total_seconds()
-        ):
-            raise TimeoutError("Waiting too long for beekeeper to be up and running")
-
-    def _handle_error(self, error: Error) -> None:
-        self.__logger.error(f"Beekeepr error: `{error.json()}`")
-
-    def _handle_status_change(self, status: Status) -> None:
-        self.__logger.info(f"Beekeeper status change to: `{status.current_status}`")
-
-    def _run(
-        self,
-        settings: Settings,
-        additional_cli_arguments: BeekeeperArguments | None = None,
-    ) -> None:
-        aca = additional_cli_arguments or BeekeeperArguments()
-        self.__setup_notification_server(address_from_cli_arguments=aca.notifications_endpoint)
-        assert self.__notification_server is not None, "Creation of notification server failed"
-        settings.notification_endpoint = HttpUrl(f"127.0.0.1:{self.__notification_server.run()}", protocol="http")
-        settings.http_endpoint = (
-            aca.webserver_http_endpoint or settings.http_endpoint or HttpUrl("127.0.0.1:0", protocol="http")
-        )
-        settings.working_directory = (
-            aca.data_dir
-            if aca.data_dir != BeekeeperArgumentsDefaults.DEFAULT_DATA_DIR
-            else self.__exec.working_directory
-        )
-        try:
-            self._run_application(settings=settings, additional_cli_arguments=aca)
-        except CalledProcessError as e:
-            raise BeekeeperFailedToStartDuringProcessSpawnError from e
-        try:
-            self.__wait_till_ready()
-        except (AssertionError, TimeoutError) as e:
-            self._close()
-            raise BeekeeperFailedToStartNotReadyOnTimeError from e
-
-    def _run_application(self, settings: Settings, additional_cli_arguments: BeekeeperArguments) -> None:
-        assert settings.notification_endpoint is not None
-        self.__exec.run(
-            blocking=False,
-            arguments=additional_cli_arguments.copy(
-                update={
-                    "notifications_endpoint": settings.notification_endpoint,
-                    "webserver_http_endpoint": settings.ensured_http_endpoint,
-                    "data_dir": settings.ensured_working_directory,
-                }
-            ),
-            propagate_sigint=settings.propagate_sigint,
-        )
+    def run(self, additional_cli_arguments: BeekeeperArguments | None = None) -> None:
+        with self._exec.restore_arguments(additional_cli_arguments):
+            try:
+                self._run()
+            except ExecutableError as e:
+                raise BeekeeperFailedToStartError from e
 
-    def detach(self) -> int:
-        pid = self.__exec.detach()
-        self.__close_notification_server()
-        return pid
+    def _write_ports(self, editable_settings: Settings, ports: PortMatchingResult) -> None:
+        editable_settings.http_endpoint = ports.http
+        self.config.webserver_http_endpoint = ports.http
+        self.config.webserver_ws_endpoint = ports.websocket
 
     def _close(self) -> None:
         self._close_application()
-        self.__close_notification_server()
 
     def _close_application(self) -> None:
-        if self.__exec.is_running():
-            self.__exec.close(self._get_settings().close_timeout.total_seconds())
-
-    def _http_webserver_ready(self, notification: Notification[WebserverListening]) -> None:
-        """It is converted by _get_http_endpoint_from_event."""
-
-    def _get_http_endpoint_from_event(self) -> HttpUrl:
-        assert self.__notification_event_handler is not None, "Notification event handler hasn't been set"
-        # <###> if you get exception from here, and have consistent way of reproduce please report <###>
-        # make sure you didn't forget to call beekeeper.run() method
-        addr = self.__notification_event_handler.http_endpoint_from_event
-        assert addr is not None, "Endpoint from event was not set"
-        return addr
+        if self._exec.is_running():
+            self._exec.close(self._get_settings().close_timeout.total_seconds())
 
     def export_keys_wallet(
         self, wallet_name: str, wallet_password: str, extract_to: Path | None = None
     ) -> list[KeyPair]:
-        return self.__exec.export_keys_wallet(
+        return self._exec.export_keys_wallet(
             wallet_name=wallet_name,
             wallet_password=wallet_password,
             extract_to=extract_to,
         )
 
-    @abstractmethod
-    def _get_settings(self) -> Settings: ...
-
-
-class Beekeeper(BeekeeperCommon, SyncRemoteBeekeeper):
-    def run(self, *, additional_cli_arguments: BeekeeperArguments | None = None) -> None:
-        self._clear_session()
-        with self.update_settings() as settings:
-            self._run(
-                settings=cast(Settings, settings),
-                additional_cli_arguments=additional_cli_arguments,
-            )
-        self.http_endpoint = self._get_http_endpoint_from_event()
 
+class Beekeeper(RunnableBeekeeper, BeekeeperTemplate[RunnableSettingsT]):
     def _get_settings(self) -> Settings:
-        assert isinstance(self.settings, Settings)
         return self.settings
 
-    @property
-    def settings(self) -> Settings:
-        return cast(Settings, super().settings)
-
-    def _enter(self) -> Beekeeper:
+    def _enter(self) -> Beekeeper[RunnableSettingsT]:
         self.run()
         return self
 
     def teardown(self) -> None:
         self._close()
+        self._clear_session()
         super().teardown()
 
-
-class AsyncBeekeeper(BeekeeperCommon, AsyncRemoteBeekeeper):
-    def run(self, *, additional_cli_arguments: BeekeeperArguments | None = None) -> None:
-        self._clear_session()
+    def _setup_ports(self, ports: PortMatchingResult) -> None:
         with self.update_settings() as settings:
-            self._run(
-                settings=cast(Settings, settings),
-                additional_cli_arguments=additional_cli_arguments,
-            )
-        self.http_endpoint = self._get_http_endpoint_from_event()
+            self._write_ports(settings, ports)
+
 
+class AsyncBeekeeper(RunnableBeekeeper, AsyncBeekeeperTemplate[RunnableSettingsT]):
     def _get_settings(self) -> Settings:
-        assert isinstance(self.settings, Settings)
         return self.settings
 
-    @property
-    def settings(self) -> Settings:
-        return cast(Settings, super().settings)
-
-    async def _aenter(self) -> AsyncBeekeeper:
+    async def _aenter(self) -> AsyncBeekeeper[RunnableSettingsT]:
         self.run()
         return self
 
     def teardown(self) -> None:
-        self._close()
+        self.close()
+        self._clear_session()
         super().teardown()
+
+    def _setup_ports(self, ports: PortMatchingResult) -> None:
+        with self.update_settings() as settings:
+            self._write_ports(settings, ports)
diff --git a/beekeepy/beekeepy/_runnable_handle/beekeeper_callbacks.py b/beekeepy/beekeepy/_runnable_handle/beekeeper_callbacks.py
deleted file mode 100644
index 8ca36a4c..00000000
--- a/beekeepy/beekeepy/_runnable_handle/beekeeper_callbacks.py
+++ /dev/null
@@ -1,42 +0,0 @@
-from __future__ import annotations
-
-import warnings
-from abc import ABC
-from typing import TYPE_CHECKING, Any, Protocol
-
-if TYPE_CHECKING:
-    from schemas.notifications import (
-        AttemptClosingWallets,
-        Error,
-        Notification,
-        OpeningBeekeeperFailed,
-        Status,
-        WebserverListening,
-    )
-
-
-class NotificationCallback(Protocol):
-    def __call__(self, notification: Notification[Any]) -> None: ...
-
-
-class BeekeeperNotificationCallbacks(ABC):  # noqa: B024
-    def __init__(self, *args: Any, **kwargs: Any) -> None:
-        super().__init__(*args, **kwargs)
-
-    def _http_webserver_ready(self, notification: Notification[WebserverListening]) -> None:
-        self.__empty_handle_message(notification.value)
-
-    def _handle_error(self, error: Error) -> None:
-        self.__empty_handle_message(error)
-
-    def _handle_status_change(self, status: Status) -> None:
-        self.__empty_handle_message(status)
-
-    def _handle_wallets_closed(self, note: AttemptClosingWallets) -> None:
-        self.__empty_handle_message(note)
-
-    def _handle_opening_beekeeper_failed(self, info: OpeningBeekeeperFailed) -> None:
-        self.__empty_handle_message(info)
-
-    def __empty_handle_message(self, obj: Any) -> None:
-        warnings.warn(f"Notification `{type(obj).__name__}` hasn't been handled", category=RuntimeWarning, stacklevel=1)
diff --git a/beekeepy/beekeepy/_runnable_handle/beekeeper_notification_handler.py b/beekeepy/beekeepy/_runnable_handle/beekeeper_notification_handler.py
deleted file mode 100644
index 74f91779..00000000
--- a/beekeepy/beekeepy/_runnable_handle/beekeeper_notification_handler.py
+++ /dev/null
@@ -1,70 +0,0 @@
-from __future__ import annotations
-
-from threading import Event
-from typing import TYPE_CHECKING, Any
-
-from loguru import logger
-
-from beekeepy._interface.url import HttpUrl
-from beekeepy._runnable_handle.notification_handler_base import BeekeeperNotificationHandler
-
-if TYPE_CHECKING:
-    from beekeepy._runnable_handle.beekeeper_callbacks import BeekeeperNotificationCallbacks
-    from schemas.notifications import (
-        AttemptClosingWallets,
-        Error,
-        KnownNotificationT,
-        Notification,
-        OpeningBeekeeperFailed,
-        Status,
-        WebserverListening,
-    )
-
-
-class NotificationHandler(BeekeeperNotificationHandler):
-    def __init__(self, owner: BeekeeperNotificationCallbacks, *args: Any, **kwargs: Any) -> None:
-        super().__init__(*args, **kwargs)
-        self.__owner = owner
-
-        self.http_listening_event = Event()
-        self.http_endpoint_from_event: HttpUrl | None = None
-
-        self.already_working_beekeeper_event = Event()
-        self.already_working_beekeeper_http_address: HttpUrl | None = None
-        self.already_working_beekeeper_pid: int | None = None
-
-    async def on_attempt_of_closing_wallets(self, notification: Notification[AttemptClosingWallets]) -> None:
-        self.__owner._handle_wallets_closed(notification.value)
-
-    async def on_opening_beekeeper_failed(self, notification: Notification[OpeningBeekeeperFailed]) -> None:
-        self.already_working_beekeeper_http_address = HttpUrl(
-            self.__combine_url_string(
-                notification.value.connection.address,
-                notification.value.connection.port,
-            ),
-            protocol="http",
-        )
-        self.already_working_beekeeper_pid = int(notification.value.pid)
-        self.already_working_beekeeper_event.set()
-        self.__owner._handle_opening_beekeeper_failed(notification.value)
-
-    async def on_error(self, notification: Notification[Error]) -> None:
-        self.__owner._handle_error(notification.value)
-
-    async def on_status_changed(self, notification: Notification[Status]) -> None:
-        self.__owner._handle_status_change(notification.value)
-
-    async def on_http_webserver_bind(self, notification: Notification[WebserverListening]) -> None:
-        self.http_endpoint_from_event = HttpUrl(
-            self.__combine_url_string(notification.value.address, notification.value.port),
-            protocol="http",
-        )
-        self.http_listening_event.set()
-        self.__owner._http_webserver_ready(notification)
-
-    async def handle_notification(self, notification: Notification[KnownNotificationT]) -> None:
-        logger.debug(f"got notification: {notification.json()}")
-        return await super().handle_notification(notification)
-
-    def __combine_url_string(self, address: str, port: int) -> str:
-        return f"{address}:{port}"
diff --git a/beekeepy/beekeepy/_runnable_handle/match_ports.py b/beekeepy/beekeepy/_runnable_handle/match_ports.py
new file mode 100644
index 00000000..09f46fb6
--- /dev/null
+++ b/beekeepy/beekeepy/_runnable_handle/match_ports.py
@@ -0,0 +1,72 @@
+from __future__ import annotations
+
+import socket
+import ssl
+from dataclasses import dataclass, field
+from typing import Final
+
+from beekeepy._communication import HttpUrl, P2PUrl, WsUrl
+
+__all__ = ["PortMatchingResult", "match_ports"]
+
+# https://http.cat/status/426
+WEBSERVER_SPECIFIC_RESPONSE: Final[bytes] = b"426 Upgrade Required"
+
+
+@dataclass
+class PortMatchingResult:
+    http: HttpUrl | None = None
+    https: HttpUrl | None = None
+    websocket: WsUrl | None = None
+    p2p: list[P2PUrl] = field(default_factory=list)
+
+
+def test_http(address: HttpUrl) -> bool:
+    assert address.port is not None, "HTTP CHECK: Port has to be set"
+    try:
+        with socket.create_connection((address.address, address.port), timeout=1) as sock:
+            sock.sendall(b"GET / HTTP/1.1\r\nHost: localhost\r\n\r\n")
+            response = sock.recv(1024)
+            return response.startswith(b"HTTP") and WEBSERVER_SPECIFIC_RESPONSE not in response
+    except (OSError, socket.timeout, ConnectionRefusedError):
+        return False
+
+
+def test_https(address: HttpUrl) -> bool:
+    assert address.port is not None, "HTTPS CHECK: Port has to be set"
+    try:
+        context = ssl.create_default_context()
+        with socket.create_connection((address.address, address.port), timeout=1) as sock, context.wrap_socket(
+            sock, server_hostname="localhost"
+        ) as ssock:
+            ssock.sendall(b"GET / HTTP/1.1\r\nHost: localhost\r\n\r\n")
+            response = ssock.recv(1024)
+            return response.startswith(b"HTTP") and WEBSERVER_SPECIFIC_RESPONSE not in response
+    except (OSError, socket.timeout, ConnectionRefusedError, ssl.SSLError):
+        return False
+
+
+def test_websocket(address: WsUrl) -> bool:
+    assert address.port is not None, "WS CHECK: Port has to be set"
+    try:
+        with socket.create_connection((address.address, address.port), timeout=1) as sock:
+            sock.sendall(b"GET / HTTP/1.1\r\nHost: localhost\r\nUpgrade: websocket\r\nConnection: Upgrade\r\n\r\n")
+            response = sock.recv(1024)
+            return WEBSERVER_SPECIFIC_RESPONSE in response
+    except (OSError, socket.timeout, ConnectionRefusedError):
+        return False
+
+
+def match_ports(ports: list[int], *, address: str = "127.0.0.1") -> PortMatchingResult:
+    categories = PortMatchingResult()
+    for port in ports:
+        if categories.http is None and test_http(http_result := HttpUrl.factory(port=port, address=address)):
+            categories.http = http_result
+        elif categories.https is None and test_https(http_result := HttpUrl.factory(port=port, address=address)):
+            categories.https = http_result
+        elif categories.websocket is None and test_websocket(ws_result := WsUrl.factory(port=port, address=address)):
+            categories.websocket = ws_result
+        else:
+            categories.p2p.append(P2PUrl.factory(port=port, address=address))
+
+    return categories
diff --git a/beekeepy/beekeepy/_runnable_handle/notification_handler_base.py b/beekeepy/beekeepy/_runnable_handle/notification_handler_base.py
deleted file mode 100644
index 1e3195be..00000000
--- a/beekeepy/beekeepy/_runnable_handle/notification_handler_base.py
+++ /dev/null
@@ -1,29 +0,0 @@
-from __future__ import annotations
-
-from typing import TYPE_CHECKING
-
-from beekeepy._communication.appbase_notification_handler import AppbaseNotificationHandler
-from beekeepy._communication.notification_decorator import notification
-from schemas.notifications import AttemptClosingWallets, OpeningBeekeeperFailed
-
-if TYPE_CHECKING:
-    from schemas.notifications import Notification
-
-
-class BeekeeperNotificationHandler(AppbaseNotificationHandler):
-    @notification(AttemptClosingWallets)
-    async def _on_attempt_of_closing_wallets(self, notification: Notification[AttemptClosingWallets]) -> None:
-        await self.on_attempt_of_closing_wallets(notification)
-
-    @notification(OpeningBeekeeperFailed)
-    async def _on_opening_beekeeper_failed(self, notification: Notification[OpeningBeekeeperFailed]) -> None:
-        await self.on_opening_beekeeper_failed(notification)
-
-    async def on_attempt_of_closing_wallets(self, notification: Notification[AttemptClosingWallets]) -> None:
-        """Called when beekeeper attempts to close wallets in session with given token."""
-
-    async def on_opening_beekeeper_failed(self, notification: Notification[OpeningBeekeeperFailed]) -> None:
-        """Called, when beekeeper failed to start.
-
-        That is because of already running other beekeeper in selected working directory.
-        """
diff --git a/beekeepy/beekeepy/_runnable_handle/runnable_handle.py b/beekeepy/beekeepy/_runnable_handle/runnable_handle.py
new file mode 100644
index 00000000..5efd171f
--- /dev/null
+++ b/beekeepy/beekeepy/_runnable_handle/runnable_handle.py
@@ -0,0 +1,256 @@
+from __future__ import annotations
+
+import time
+import warnings
+from abc import ABC, abstractmethod
+from datetime import timedelta
+from pathlib import Path
+from subprocess import SubprocessError
+from typing import TYPE_CHECKING, Any, Generic, TypeVar, cast
+
+from loguru import logger as default_logger
+
+from beekeepy._communication import HttpUrl, P2PUrl, WsUrl
+from beekeepy._executable import ArgumentT, ConfigT, Executable
+from beekeepy._remote_handle import AppStatusProbe
+from beekeepy._runnable_handle.match_ports import PortMatchingResult, match_ports
+from beekeepy._runnable_handle.settings import Settings
+from beekeepy.exceptions import (
+    ApiNotFoundError,
+    FailedToDetectReservedPortsError,
+    FailedToStartExecutableError,
+)
+
+if TYPE_CHECKING:
+    from loguru import Logger
+
+    from schemas.apis.app_status_api import GetAppStatus
+
+
+ExecutableT = TypeVar("ExecutableT", bound=Executable[Any, Any])
+SettingsT = TypeVar("SettingsT", bound=Settings)
+T = TypeVar("T")
+
+
+class RunnableHandle(ABC, Generic[ExecutableT, ConfigT, ArgumentT, SettingsT]):
+    def __init__(self, *args: Any, logger: Logger | None = None, **kwargs: Any) -> None:
+        super().__init__(*args, **kwargs)
+        self._logger = logger or default_logger
+        self._exec = self._construct_executable()
+
+    @property
+    def pid(self) -> int:
+        """Returns pid of started executable. Note: Proxy method to Executable.pid."""
+        return self._exec.pid
+
+    @property
+    def arguments(self) -> ArgumentT:
+        """Returns arguments for given binary. Note: Proxy method to Executable.arguments."""
+        return cast(ArgumentT, self._exec.arguments)
+
+    @property
+    def config(self) -> ConfigT:
+        """Returns config for given binary. Note: Proxy method to Executable.config."""
+        return cast(ConfigT, self._exec.config)
+
+    def is_running(self) -> bool:
+        """Returns is process running. Note: Proxy method to Executable.is_running."""
+        return self._exec.is_running()
+
+    def detach(self) -> int:
+        """Detaches process and allows to keep it after closing python script."""
+        return self._exec.detach()
+
+    def close(self) -> None:
+        """Closes running process. If process is not running, method does nothing."""
+        if self.is_running():
+            self._exec.close(timeout_secs=self._get_settings().close_timeout.total_seconds())
+
+    def get_help_text(self) -> str:
+        """Returns help printed by executable."""
+        self.__show_warning_if_executable_already_running()
+        return self._exec.get_help_text()
+
+    def get_version(self) -> str:
+        """Returns version string printed by executable."""
+        self.__show_warning_if_executable_already_running()
+        return self._exec.version()
+
+    def generate_default_config_from_executable(self) -> ConfigT:
+        """Returns config generated by executable."""
+        self.__show_warning_if_executable_already_running()
+        return cast(ConfigT, self._exec.generate_default_config())
+
+    def _run(
+        self,
+        *,
+        environment_variables: dict[str, str] | None = None,
+        perform_unification: bool = True,
+        blocking: bool = False,
+        save_config: bool = True,
+    ) -> None:
+        """
+        Runs executable and unifies arguments.
+
+        Note: This method should be called by RunnableHandleChild.run, which is not defined by this interface!
+
+        Keyword Arguments:
+            environment_variables -- additional environment variables to set before launching executable
+            additional_cli_arguments -- arguments to add to executable invocation
+            perform_unification -- if set to true, chosen values will be written to config and cli arguments
+        """
+        settings = self._get_settings().copy()
+
+        settings.working_directory = self.__choose_working_directory(settings=settings)
+        settings.http_endpoint = self.__choose_http_endpoint(settings=settings)
+
+        if perform_unification:
+            self._unify_cli_arguments(settings.working_directory, settings.http_endpoint)
+            self._unify_config(settings.working_directory, settings.http_endpoint)
+
+        try:
+            self._exec._run(
+                blocking=blocking,
+                environ=environment_variables,
+                propagate_sigint=settings.propagate_sigint,
+                save_config=save_config,
+            )
+            if blocking:
+                return
+        except SubprocessError as e:
+            raise FailedToStartExecutableError from e
+        try:
+            self._wait_for_app_to_start()
+        except TimeoutError as e:
+            raise FailedToDetectReservedPortsError from e
+        self._setup_ports(self.__discover_ports())
+
+    @abstractmethod
+    def _construct_executable(self) -> ExecutableT:
+        """Returns executable instance."""
+
+    @abstractmethod
+    def _get_settings(self) -> SettingsT:
+        """Returns settings hold by child class. Used only for read-only purposes."""
+
+    def _get_working_directory_from_cli_arguments(self) -> Path | None:
+        """Returns working directory from specified cli arguments in executable (if specified)."""
+        return None
+
+    def _get_http_endpoint_from_cli_arguments(self) -> HttpUrl | None:
+        """Returns http endpoint from specified cli arguments in executable (if specified)."""
+        return None
+
+    def _get_working_directory_from_config(self) -> Path | None:
+        """Returns working directory from specified config in executable (if specified)."""
+        return None
+
+    def _get_http_endpoint_from_config(self) -> HttpUrl | None:
+        """Returns http endpoint from specified config in executable (if specified)."""
+        return None
+
+    @abstractmethod
+    def _unify_cli_arguments(self, working_directory: Path, http_endpoint: HttpUrl) -> None:
+        """
+        Writes selected values to given cli arguments.
+
+        Args:
+            working_directory -- chosen working path to be set in cli arguments.
+            http_endpoint -- chosen http endpoint to be set in cli arguments.
+        """
+
+    @abstractmethod
+    def _unify_config(self, working_directory: Path, http_endpoint: HttpUrl) -> None:
+        """
+        Writes selected values to config in executable.
+
+        Args:
+            working_directory -- chosen working path to be set in config.
+            http_endpoint -- chosen http endpoint to be set in config.
+        """
+
+    def _setup_ports(self, ports: PortMatchingResult) -> None:
+        """
+        Setup ports after startup.
+
+        Args:
+            ports -- list of ports reserved by started application.
+        """
+
+    def _wait_for_app_to_start(self) -> None:
+        """Waits for application to start."""
+        while not self._exec.reserved_ports():
+            if not self._exec.is_running():
+                raise FailedToStartExecutableError
+            time.sleep(0.1)
+
+    def __choose_working_directory(self, settings: Settings) -> Path:
+        return self.__choose_value(
+            default_value=Path.cwd(),
+            argument_value=self._get_working_directory_from_cli_arguments(),
+            config_value=self._get_working_directory_from_config(),
+            settings_value=settings.working_directory,
+        )
+
+    def __choose_http_endpoint(self, settings: Settings) -> HttpUrl:
+        return self.__choose_value(
+            default_value=HttpUrl("http://0.0.0.0:0"),
+            argument_value=self._get_http_endpoint_from_cli_arguments(),
+            config_value=self._get_http_endpoint_from_config(),
+            settings_value=settings.http_endpoint,
+        )
+
+    def __show_warning_if_executable_already_running(self) -> None:
+        if self.is_running():
+            warnings.warn("Invoking executable that is already running!", stacklevel=2)
+
+    @classmethod
+    def __choose_value(
+        cls,
+        default_value: T,
+        argument_value: T | None = None,
+        config_value: T | None = None,
+        settings_value: T | None = None,
+    ) -> T:
+        if argument_value is not None:
+            return argument_value
+        if config_value is not None:
+            return config_value
+        if settings_value is not None:
+            return settings_value
+        return default_value
+
+    def __discover_ports(self) -> PortMatchingResult:
+        reserved_ports = self._exec.reserved_ports()
+        matched_ports = match_ports(reserved_ports)
+        if matched_ports.http is None:
+            warnings.warn("Given executable probably does not provide http network access", stacklevel=3)
+            return matched_ports
+
+        handle = AppStatusProbe(settings=Settings(http_endpoint=matched_ports.http, timeout=timedelta(seconds=1)))
+        status: None | GetAppStatus = None
+        try:
+            status = handle.api.get_app_status()
+        except ApiNotFoundError:
+            warnings.warn(
+                "HTTP port detected, but cannot obtain further information. app_status_api plugin is not enabled!",
+                stacklevel=3,
+            )
+            return matched_ports
+
+        assert status is not None, "Error has not been caught and further port discovery started"
+        http = status.webservers.HTTP
+        assert http, "Http cannot be None, as AppStatusProbe is already connected via http"
+        assert (
+            http.port == matched_ports.http.port
+        ), "Http cannot differ from detected ports, because it is already connected"
+
+        ws = status.webservers.WS
+        if ws and matched_ports.websocket and ws.port != matched_ports.websocket.port:
+            matched_ports.websocket = WsUrl.factory(port=ws.port)
+
+        p2p = status.webservers.P2P
+        if p2p and p2p.port not in [x.port for x in matched_ports.p2p]:
+            matched_ports.p2p = [P2PUrl.factory(port=p2p.port)]
+
+        return matched_ports
diff --git a/beekeepy/beekeepy/_runnable_handle/settings.py b/beekeepy/beekeepy/_runnable_handle/settings.py
index c989f3e4..1ee8b0c2 100644
--- a/beekeepy/beekeepy/_runnable_handle/settings.py
+++ b/beekeepy/beekeepy/_runnable_handle/settings.py
@@ -5,13 +5,12 @@ from distutils.util import strtobool
 from pathlib import Path
 from typing import ClassVar
 
-from beekeepy._communication.abc.communicator import AbstractCommunicator  # noqa: TCH001
-from beekeepy._interface.url import HttpUrl  # noqa: TCH001
-from beekeepy._remote_handle.settings import Settings as RemoteHandleSettings
+from beekeepy._communication import HttpUrl  # noqa: TCH001
+from beekeepy._remote_handle import RemoteHandleSettings
 
 
 class Settings(RemoteHandleSettings):
-    """Defines parameters for beekeeper how to start and behave."""
+    """Defines parameters for runnable handles how to start and behave."""
 
     class EnvironNames(RemoteHandleSettings.EnvironNames):
         WORKING_DIRECTORY: ClassVar[str] = "BEEKEEPY_WORKING_DIRECTORY"
@@ -34,16 +33,6 @@ class Settings(RemoteHandleSettings):
     In case of local beekeeper, this address will be used for beekeeper to start listening on.
     """
 
-    communicator: type[AbstractCommunicator] | AbstractCommunicator | None = None
-    """
-    Defines class to be used for network handling. Can be given as class or instance.
-
-    Note: If set to none, handles will use preferred communicators
-    """
-
-    notification_endpoint: HttpUrl | None = None
-    """Endpoint to use for reverse communication between beekeeper and python."""
-
     binary_path: Path | None = None
     """Alternative path to beekeeper binary."""
 
@@ -66,6 +55,7 @@ class Settings(RemoteHandleSettings):
         EnvironNames.INITIALIZATION_TIMEOUT,
         lambda x: (Settings.Defaults.INITIALIZATION_TIMEOUT if x is None else timedelta(seconds=int(x))),
     )
+    """Affects time handle waits for beekeeper to start."""
 
     @property
     def ensured_working_directory(self) -> Path:
diff --git a/beekeepy/beekeepy/_executable/arguments/__init__.py b/beekeepy/beekeepy/_utilities/__init__.py
similarity index 100%
rename from beekeepy/beekeepy/_executable/arguments/__init__.py
rename to beekeepy/beekeepy/_utilities/__init__.py
diff --git a/beekeepy/beekeepy/_remote_handle/build_json_rpc_call.py b/beekeepy/beekeepy/_utilities/build_json_rpc_call.py
similarity index 100%
rename from beekeepy/beekeepy/_remote_handle/build_json_rpc_call.py
rename to beekeepy/beekeepy/_utilities/build_json_rpc_call.py
diff --git a/beekeepy/beekeepy/_interface/context.py b/beekeepy/beekeepy/_utilities/context.py
similarity index 100%
rename from beekeepy/beekeepy/_interface/context.py
rename to beekeepy/beekeepy/_utilities/context.py
diff --git a/beekeepy/beekeepy/_interface/context_settings_updater.py b/beekeepy/beekeepy/_utilities/context_settings_updater.py
similarity index 93%
rename from beekeepy/beekeepy/_interface/context_settings_updater.py
rename to beekeepy/beekeepy/_utilities/context_settings_updater.py
index a10bbc93..ca41424a 100644
--- a/beekeepy/beekeepy/_interface/context_settings_updater.py
+++ b/beekeepy/beekeepy/_utilities/context_settings_updater.py
@@ -4,12 +4,12 @@ from abc import abstractmethod
 from contextlib import contextmanager
 from typing import TYPE_CHECKING, Generic, TypeVar
 
-from beekeepy._communication.settings import CommunicationSettings
+from pydantic import BaseModel
 
 if TYPE_CHECKING:
     from collections.abc import Iterator
 
-SettingsT = TypeVar("SettingsT", bound=CommunicationSettings)
+SettingsT = TypeVar("SettingsT", bound=BaseModel)
 
 
 class ContextSettingsUpdater(Generic[SettingsT]):
diff --git a/beekeepy/beekeepy/_interface/delay_guard.py b/beekeepy/beekeepy/_utilities/delay_guard.py
similarity index 97%
rename from beekeepy/beekeepy/_interface/delay_guard.py
rename to beekeepy/beekeepy/_utilities/delay_guard.py
index 670bcc46..918ed820 100644
--- a/beekeepy/beekeepy/_interface/delay_guard.py
+++ b/beekeepy/beekeepy/_utilities/delay_guard.py
@@ -5,7 +5,7 @@ import time
 from datetime import datetime, timedelta, timezone
 from typing import TYPE_CHECKING, Final
 
-from beekeepy._interface.context import ContextAsync, ContextSync
+from beekeepy._utilities.context import ContextAsync, ContextSync
 from beekeepy.exceptions import UnlockIsNotAccessibleError
 
 if TYPE_CHECKING:
diff --git a/beekeepy/beekeepy/_interface/error_logger.py b/beekeepy/beekeepy/_utilities/error_logger.py
similarity index 93%
rename from beekeepy/beekeepy/_interface/error_logger.py
rename to beekeepy/beekeepy/_utilities/error_logger.py
index 76037104..dd2872cc 100644
--- a/beekeepy/beekeepy/_interface/error_logger.py
+++ b/beekeepy/beekeepy/_utilities/error_logger.py
@@ -4,7 +4,7 @@ from typing import TYPE_CHECKING
 
 from loguru import logger as loguru_logger
 
-from beekeepy._interface.context import ContextSync
+from beekeepy._utilities.context import ContextSync
 
 if TYPE_CHECKING:
     from types import TracebackType
@@ -13,7 +13,7 @@ if TYPE_CHECKING:
 
 
 class ErrorLogger(ContextSync[None]):
-    def __init__(self, logger: Logger | None = None, *exceptions: type[Exception]) -> None:
+    def __init__(self, logger: Logger | None = None, *exceptions: type[BaseException]) -> None:
         super().__init__()
         self.__logger = logger or loguru_logger
         self.__exception_whitelist = list(exceptions)
diff --git a/beekeepy/beekeepy/_interface/key_pair.py b/beekeepy/beekeepy/_utilities/key_pair.py
similarity index 100%
rename from beekeepy/beekeepy/_interface/key_pair.py
rename to beekeepy/beekeepy/_utilities/key_pair.py
diff --git a/beekeepy/beekeepy/_interface/_sanitize.py b/beekeepy/beekeepy/_utilities/sanitize.py
similarity index 100%
rename from beekeepy/beekeepy/_interface/_sanitize.py
rename to beekeepy/beekeepy/_utilities/sanitize.py
diff --git a/beekeepy/beekeepy/_interface/settings_holder.py b/beekeepy/beekeepy/_utilities/settings_holder.py
similarity index 97%
rename from beekeepy/beekeepy/_interface/settings_holder.py
rename to beekeepy/beekeepy/_utilities/settings_holder.py
index ae98400d..e4a3ccae 100644
--- a/beekeepy/beekeepy/_interface/settings_holder.py
+++ b/beekeepy/beekeepy/_utilities/settings_holder.py
@@ -4,7 +4,7 @@ from abc import ABC, abstractmethod
 from contextlib import contextmanager
 from typing import TYPE_CHECKING, Any
 
-from beekeepy._interface.context_settings_updater import ContextSettingsUpdater, SettingsT
+from beekeepy._utilities.context_settings_updater import ContextSettingsUpdater, SettingsT
 
 if TYPE_CHECKING:
     from collections.abc import Iterator
diff --git a/beekeepy/beekeepy/_interface/state_invalidator.py b/beekeepy/beekeepy/_utilities/state_invalidator.py
similarity index 100%
rename from beekeepy/beekeepy/_interface/state_invalidator.py
rename to beekeepy/beekeepy/_utilities/state_invalidator.py
diff --git a/beekeepy/beekeepy/_interface/stopwatch.py b/beekeepy/beekeepy/_utilities/stopwatch.py
similarity index 95%
rename from beekeepy/beekeepy/_interface/stopwatch.py
rename to beekeepy/beekeepy/_utilities/stopwatch.py
index 094f09a4..61b751fe 100644
--- a/beekeepy/beekeepy/_interface/stopwatch.py
+++ b/beekeepy/beekeepy/_utilities/stopwatch.py
@@ -4,7 +4,7 @@ from dataclasses import dataclass
 from datetime import datetime, timedelta, timezone
 from typing import Any
 
-from beekeepy._interface.context import ContextSync
+from beekeepy._utilities.context import ContextSync
 
 
 @dataclass
diff --git a/beekeepy/beekeepy/_interface/_suppress_api_not_found.py b/beekeepy/beekeepy/_utilities/suppress_api_not_found.py
similarity index 96%
rename from beekeepy/beekeepy/_interface/_suppress_api_not_found.py
rename to beekeepy/beekeepy/_utilities/suppress_api_not_found.py
index 4b7758cd..2fb37cbf 100644
--- a/beekeepy/beekeepy/_interface/_suppress_api_not_found.py
+++ b/beekeepy/beekeepy/_utilities/suppress_api_not_found.py
@@ -2,7 +2,7 @@ from __future__ import annotations
 
 from typing import TYPE_CHECKING
 
-from beekeepy._interface.context import SelfContextSync
+from beekeepy._utilities.context import SelfContextSync
 from beekeepy.exceptions import ApiNotFoundError, GroupedErrorsError
 
 if TYPE_CHECKING:
diff --git a/beekeepy/beekeepy/exceptions/__init__.py b/beekeepy/beekeepy/exceptions/__init__.py
index 6faeb756..cfcf6832 100644
--- a/beekeepy/beekeepy/exceptions/__init__.py
+++ b/beekeepy/beekeepy/exceptions/__init__.py
@@ -7,8 +7,8 @@ from beekeepy.exceptions.base import (
     BeekeeperInterfaceError,
     BeekeepyError,
     CommunicationError,
-    CommunicationResponseT,
     DetectableError,
+    ExecutableError,
     InvalidatedStateError,
     Json,
     OverseerError,
@@ -46,6 +46,14 @@ from beekeepy.exceptions.detectable import (
     NoWalletWithSuchNameError,
     WalletWithSuchNameAlreadyExistsError,
 )
+from beekeepy.exceptions.executable import (
+    ExecutableIsNotRunningError,
+    FailedToDetectReservedPortsError,
+    FailedToStartExecutableError,
+)
+from beekeepy.exceptions.executable import (
+    TimeoutReachWhileCloseError as ExecutableTimeoutReachWhileCloseError,
+)
 from beekeepy.exceptions.overseer import (
     ApiNotFoundError,
     DifferenceBetweenAmountOfRequestsAndResponsesError,
@@ -74,11 +82,15 @@ __all__ = [
     "BeekeeperIsNotRunningError",
     "BeekeepyError",
     "CommunicationError",
-    "CommunicationResponseT",
     "DetachRemoteBeekeeperError",
     "DetectableError",
     "DifferenceBetweenAmountOfRequestsAndResponsesError",
     "ErrorInResponseError",
+    "ExecutableError",
+    "ExecutableIsNotRunningError",
+    "ExecutableTimeoutReachWhileCloseError",
+    "FailedToDetectReservedPortsError",
+    "FailedToStartExecutableError",
     "GroupedErrorsError",
     "InvalidAccountNameError",
     "InvalidatedStateByClosingBeekeeperError",
@@ -100,8 +112,8 @@ __all__ = [
     "NotPositiveTimeError",
     "NoWalletWithSuchNameError",
     "NullResultError",
-    "OverseerInvalidPasswordError",
     "OverseerError",
+    "OverseerInvalidPasswordError",
     "ResponseNotReadyError",
     "SchemaDetectableError",
     "TimeoutExceededError",
diff --git a/beekeepy/beekeepy/exceptions/base.py b/beekeepy/beekeepy/exceptions/base.py
index 61c77cc9..ba088f05 100644
--- a/beekeepy/beekeepy/exceptions/base.py
+++ b/beekeepy/beekeepy/exceptions/base.py
@@ -5,18 +5,18 @@ from typing import TYPE_CHECKING, Any
 
 from pydantic import StrRegexError
 
-from beekeepy._interface.context import ContextSync
+from beekeepy._utilities.context import ContextSync
 
 if TYPE_CHECKING:
     from types import TracebackType
 
-    from beekeepy._interface.url import Url
+    from beekeepy._communication import Url
 
 Json = dict[str, Any]
 CommunicationResponseT = str | Json | list[Json]
 
 
-class BeekeepyError(Exception):
+class BeekeepyError(BaseException, ABC):
     """Base class for all exception raised by beekeepy."""
 
     @property
@@ -189,3 +189,7 @@ class OverseerError(CommunicationError, ABC):
     @abstractmethod
     def retry(self) -> bool:
         """Used by overseer to determine if retry should be performed if such error occurs."""
+
+
+class ExecutableError(BeekeepyError, ABC):
+    """Base class for errors related to handling executable."""
diff --git a/beekeepy/beekeepy/exceptions/common.py b/beekeepy/beekeepy/exceptions/common.py
index 29b3efd0..eccdb9ab 100644
--- a/beekeepy/beekeepy/exceptions/common.py
+++ b/beekeepy/beekeepy/exceptions/common.py
@@ -13,7 +13,7 @@ from beekeepy.exceptions.base import (
 )
 
 if TYPE_CHECKING:
-    from beekeepy._interface.url import Url
+    from beekeepy._communication import Url
 
 
 class BatchRequestError(BeekeepyError):
@@ -93,6 +93,14 @@ class TimeTooBigError(BeekeepyError):
         super().__init__(f"Given time value is too big: `{time}` >= {TimeTooBigError.MAX_VALUE}.")
 
 
+class InvalidOptionError(BeekeepyError):
+    """Raised if invalid expression is given in config."""
+
+
+class UnknownDecisionPathError(BeekeepyError):
+    """Error created to suppress mypy error: `Missing return statement  [return]`."""
+
+
 class DetachRemoteBeekeeperError(BeekeeperHandleError):
     """Raises when user tries to detach beekeeper that is remote."""
 
@@ -128,14 +136,6 @@ class InvalidatedStateByClosingSessionError(InvalidatedStateError):
         )
 
 
-class InvalidOptionError(BeekeepyError):
-    """Raised if invalid expression is given in config."""
-
-
-class UnknownDecisionPathError(BeekeepyError):
-    """Error created to suppress mypy error: `Missing return statement  [return]`."""
-
-
 class TimeoutExceededError(CommunicationError):
     """Raised if exceeded time for response."""
 
diff --git a/beekeepy/beekeepy/exceptions/executable.py b/beekeepy/beekeepy/exceptions/executable.py
new file mode 100644
index 00000000..fafda8de
--- /dev/null
+++ b/beekeepy/beekeepy/exceptions/executable.py
@@ -0,0 +1,23 @@
+from __future__ import annotations
+
+from beekeepy.exceptions.base import ExecutableError
+
+
+class TimeoutReachWhileCloseError(ExecutableError):
+    """Raises when executable did not closed during specified timeout."""
+
+    def __init__(self) -> None:
+        """Constructor."""
+        super().__init__("Process was force-closed with SIGKILL, because didn't close before timeout")
+
+
+class ExecutableIsNotRunningError(ExecutableError):
+    """Raises when executable is not running, but user requests action on running instance."""
+
+
+class FailedToStartExecutableError(ExecutableError):
+    """Raises when executable failed to start."""
+
+
+class FailedToDetectReservedPortsError(ExecutableError):
+    """Raises when port lookup procedure fails."""
diff --git a/beekeepy/beekeepy/exceptions/overseer.py b/beekeepy/beekeepy/exceptions/overseer.py
index 1fdc616d..4007e825 100644
--- a/beekeepy/beekeepy/exceptions/overseer.py
+++ b/beekeepy/beekeepy/exceptions/overseer.py
@@ -5,7 +5,7 @@ from typing import TYPE_CHECKING, Any, Sequence
 from beekeepy.exceptions.base import BeekeepyError, CommunicationResponseT, Json, OverseerError
 
 if TYPE_CHECKING:
-    from beekeepy._interface.url import Url
+    from beekeepy._communication import Url
 
 
 class UnableToAcquireDatabaseLockError(OverseerError):
@@ -110,7 +110,7 @@ class ErrorInResponseError(OverseerError):
 
 
 class GroupedErrorsError(BeekeepyError):
-    def __init__(self, exceptions: Sequence[Exception]) -> None:
+    def __init__(self, exceptions: Sequence[BaseException]) -> None:
         self.exceptions = list(exceptions)
 
     def get_exception_for(self, *, request_id: int) -> OverseerError | None:
diff --git a/beekeepy/beekeepy/handle/remote.py b/beekeepy/beekeepy/handle/remote.py
index e0a6bea4..d79c765f 100644
--- a/beekeepy/beekeepy/handle/remote.py
+++ b/beekeepy/beekeepy/handle/remote.py
@@ -1,11 +1,21 @@
 from __future__ import annotations
 
-from beekeepy._remote_handle.abc.api import AbstractAsyncApi, AbstractSyncApi, AsyncHandleT
-from beekeepy._remote_handle.abc.api_collection import AbstractAsyncApiCollection, AbstractSyncApiCollection
-from beekeepy._remote_handle.abc.handle import AbstractAsyncHandle, AbstractSyncHandle
-from beekeepy._remote_handle.batch_handle import AsyncBatchHandle, SyncBatchHandle
-from beekeepy._remote_handle.beekeeper import AsyncBeekeeper, Beekeeper
-from beekeepy._remote_handle.settings import Settings as RemoteSettings
+from beekeepy._apis.abc import (
+    AbstractAsyncApi,
+    AbstractAsyncApiCollection,
+    AbstractSyncApi,
+    AbstractSyncApiCollection,
+    RegisteredApisT,
+)
+from beekeepy._remote_handle import (
+    AbstractAsyncHandle,
+    AbstractSyncHandle,
+    AsyncBatchHandle,
+    AsyncBeekeeper,
+    Beekeeper,
+    SyncBatchHandle,
+)
+from beekeepy._remote_handle import RemoteHandleSettings as RemoteSettings
 
 __all__ = [
     "AbstractAsyncApi",
@@ -16,8 +26,8 @@ __all__ = [
     "AbstractSyncHandle",
     "AsyncBatchHandle",
     "AsyncBeekeeper",
-    "AsyncHandleT",
     "Beekeeper",
+    "RegisteredApisT",
     "RemoteSettings",
     "SyncBatchHandle",
 ]
diff --git a/beekeepy/beekeepy/handle/runnable.py b/beekeepy/beekeepy/handle/runnable.py
index 0b2b567c..247ed9e6 100644
--- a/beekeepy/beekeepy/handle/runnable.py
+++ b/beekeepy/beekeepy/handle/runnable.py
@@ -1,27 +1,14 @@
 from __future__ import annotations
 
-from beekeepy._communication.appbase_notification_handler import AppbaseNotificationHandler
-from beekeepy._communication.async_server import AsyncHttpServer
-from beekeepy._communication.notification_decorator import notification
-from beekeepy._communication.universal_notification_server import UniversalNotificationHandler
-from beekeepy._executable.arguments.beekeeper_arguments import BeekeeperArguments, BeekeeperArgumentsDefaults
-from beekeepy._executable.beekeeper_config import BeekeeperConfig
-from beekeepy._runnable_handle.beekeeper import AsyncBeekeeper, Beekeeper
-from beekeepy._runnable_handle.close_already_running_beekeeper import close_already_running_beekeeper
-from beekeepy._runnable_handle.notification_handler_base import BeekeeperNotificationHandler
-from beekeepy._runnable_handle.settings import Settings as RunnableSettings
+from beekeepy._executable import BeekeeperArguments, BeekeeperConfig, BeekeeperExecutable
+from beekeepy._runnable_handle import AsyncBeekeeper, Beekeeper, RunnableHandleSettings, close_already_running_beekeeper
 
 __all__ = [
-    "AppbaseNotificationHandler",
     "AsyncBeekeeper",
-    "AsyncHttpServer",
     "Beekeeper",
-    "BeekeeperArguments",
-    "BeekeeperArgumentsDefaults",
     "BeekeeperConfig",
-    "BeekeeperNotificationHandler",
+    "BeekeeperArguments",
+    "BeekeeperExecutable",
     "close_already_running_beekeeper",
-    "notification",
-    "RunnableSettings",
-    "UniversalNotificationHandler",
+    "RunnableHandleSettings",
 ]
diff --git a/beekeepy/beekeepy/interfaces.py b/beekeepy/beekeepy/interfaces.py
index 65742fb8..0cc6f4b8 100644
--- a/beekeepy/beekeepy/interfaces.py
+++ b/beekeepy/beekeepy/interfaces.py
@@ -1,22 +1,14 @@
 from __future__ import annotations
 
-from beekeepy._interface._sanitize import mask, sanitize
-from beekeepy._interface._suppress_api_not_found import SuppressApiNotFound
-from beekeepy._interface.context import (
-    ContextAsync,
-    ContextSync,
-    SelfContextAsync,
-    SelfContextSync,
-)
-from beekeepy._interface.context_settings_updater import ContextSettingsUpdater
-from beekeepy._interface.error_logger import ErrorLogger
-from beekeepy._interface.key_pair import KeyPair
-from beekeepy._interface.settings_holder import (
-    SharedSettingsHolder,
-    UniqueSettingsHolder,
-)
-from beekeepy._interface.stopwatch import Stopwatch, StopwatchResult
-from beekeepy._interface.url import HttpUrl, P2PUrl, Url, WsUrl
+from beekeepy._communication import HttpUrl, P2PUrl, Url, WsUrl
+from beekeepy._utilities.context import ContextAsync, ContextSync, SelfContextAsync, SelfContextSync
+from beekeepy._utilities.context_settings_updater import ContextSettingsUpdater
+from beekeepy._utilities.error_logger import ErrorLogger
+from beekeepy._utilities.key_pair import KeyPair
+from beekeepy._utilities.sanitize import mask, sanitize
+from beekeepy._utilities.settings_holder import SharedSettingsHolder, UniqueSettingsHolder
+from beekeepy._utilities.stopwatch import Stopwatch, StopwatchResult
+from beekeepy._utilities.suppress_api_not_found import SuppressApiNotFound
 
 __all__ = [
     "ContextAsync",
diff --git a/beekeepy/poetry.lock b/beekeepy/poetry.lock
index 03dc514c..b5137a66 100644
--- a/beekeepy/poetry.lock
+++ b/beekeepy/poetry.lock
@@ -1,4 +1,4 @@
-# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand.
+# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand.
 
 [[package]]
 name = "aiohttp"
@@ -633,6 +633,35 @@ files = [
     {file = "propcache-0.2.1.tar.gz", hash = "sha256:3f77ce728b19cb537714499928fe800c3dda29e8d9428778fc7c186da4c09a64"},
 ]
 
+[[package]]
+name = "psutil"
+version = "6.0.0"
+description = "Cross-platform lib for process and system monitoring in Python."
+optional = false
+python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7"
+files = [
+    {file = "psutil-6.0.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a021da3e881cd935e64a3d0a20983bda0bb4cf80e4f74fa9bfcb1bc5785360c6"},
+    {file = "psutil-6.0.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:1287c2b95f1c0a364d23bc6f2ea2365a8d4d9b726a3be7294296ff7ba97c17f0"},
+    {file = "psutil-6.0.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:a9a3dbfb4de4f18174528d87cc352d1f788b7496991cca33c6996f40c9e3c92c"},
+    {file = "psutil-6.0.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:6ec7588fb3ddaec7344a825afe298db83fe01bfaaab39155fa84cf1c0d6b13c3"},
+    {file = "psutil-6.0.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:1e7c870afcb7d91fdea2b37c24aeb08f98b6d67257a5cb0a8bc3ac68d0f1a68c"},
+    {file = "psutil-6.0.0-cp27-none-win32.whl", hash = "sha256:02b69001f44cc73c1c5279d02b30a817e339ceb258ad75997325e0e6169d8b35"},
+    {file = "psutil-6.0.0-cp27-none-win_amd64.whl", hash = "sha256:21f1fb635deccd510f69f485b87433460a603919b45e2a324ad65b0cc74f8fb1"},
+    {file = "psutil-6.0.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:c588a7e9b1173b6e866756dde596fd4cad94f9399daf99ad8c3258b3cb2b47a0"},
+    {file = "psutil-6.0.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ed2440ada7ef7d0d608f20ad89a04ec47d2d3ab7190896cd62ca5fc4fe08bf0"},
+    {file = "psutil-6.0.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5fd9a97c8e94059b0ef54a7d4baf13b405011176c3b6ff257c247cae0d560ecd"},
+    {file = "psutil-6.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2e8d0054fc88153ca0544f5c4d554d42e33df2e009c4ff42284ac9ebdef4132"},
+    {file = "psutil-6.0.0-cp36-cp36m-win32.whl", hash = "sha256:fc8c9510cde0146432bbdb433322861ee8c3efbf8589865c8bf8d21cb30c4d14"},
+    {file = "psutil-6.0.0-cp36-cp36m-win_amd64.whl", hash = "sha256:34859b8d8f423b86e4385ff3665d3f4d94be3cdf48221fbe476e883514fdb71c"},
+    {file = "psutil-6.0.0-cp37-abi3-win32.whl", hash = "sha256:a495580d6bae27291324fe60cea0b5a7c23fa36a7cd35035a16d93bdcf076b9d"},
+    {file = "psutil-6.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:33ea5e1c975250a720b3a6609c490db40dae5d83a4eb315170c4fe0d8b1f34b3"},
+    {file = "psutil-6.0.0-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:ffe7fc9b6b36beadc8c322f84e1caff51e8703b88eee1da46d1e3a6ae11b4fd0"},
+    {file = "psutil-6.0.0.tar.gz", hash = "sha256:8faae4f310b6d969fa26ca0545338b21f73c6b15db7c4a8d934a5482faa818f2"},
+]
+
+[package.extras]
+test = ["enum34", "ipaddress", "mock", "pywin32", "wmi"]
+
 [[package]]
 name = "pydantic"
 version = "1.10.18"
@@ -746,12 +775,12 @@ idna2008 = ["idna"]
 
 [[package]]
 name = "schemas"
-version = "0.0.1.dev323+e5a1ba1"
+version = "0.0.1.dev333+540050d"
 description = "Tools for checking if message fits expected format"
 optional = false
 python-versions = ">=3.10,<4.0"
 files = [
-    {file = "schemas-0.0.1.dev323+e5a1ba1-py3-none-any.whl", hash = "sha256:b97546f24f54f58d71fade176e3fdfa8e113c040d35f56f04e5a567b0fa63d5f"},
+    {file = "schemas-0.0.1.dev333+540050d-py3-none-any.whl", hash = "sha256:0bdaab260640262a8b4b64e1a4c9fb7aefcedc0f418eb1a646fbad491a934ac7"},
 ]
 
 [package.dependencies]
@@ -924,4 +953,4 @@ propcache = ">=0.2.0"
 [metadata]
 lock-version = "2.0"
 python-versions = "^3.10"
-content-hash = "a8172408e6af9484194ca2369b1bbd3049cfc50956de26b95b59f9adf47cd805"
+content-hash = "8b8fb493bd5f2936397077133f358724fda104dc89766ace31c422b805f384a4"
diff --git a/beekeepy/pyproject.toml b/beekeepy/pyproject.toml
index 48aae02f..dd6998bb 100644
--- a/beekeepy/pyproject.toml
+++ b/beekeepy/pyproject.toml
@@ -23,14 +23,14 @@ source = [
 version = "0.0.0"
 
 [tool.poetry.dependencies]
+python = "^3.10"
 aiohttp = "3.9.1"
 httpx = {extras = ["http2"], version = "0.23.3"}
 loguru = "0.7.2"
-python = "^3.10"
 python-dateutil = "2.8.2"
-pydantic="1.10.18"
+psutil = "6.0.0"
 requests = "2.27.1"
-schemas = "0.0.1.dev323+e5a1ba1"
+schemas = "0.0.1.dev333+540050d"
 
 [tool.poetry-dynamic-versioning]
 enable = true
diff --git a/hive b/hive
index ed702f86..40e6bc38 160000
--- a/hive
+++ b/hive
@@ -1 +1 @@
-Subproject commit ed702f86d3a1ae567b2d2e1d0d7240a187223964
+Subproject commit 40e6bc384b63fe116f370153a20c15a46888011f
diff --git a/tests/beekeepy_test/handle/api_tests/test_api_close.py b/tests/beekeepy_test/handle/api_tests/test_api_close.py
index 53999acf..dc521f72 100644
--- a/tests/beekeepy_test/handle/api_tests/test_api_close.py
+++ b/tests/beekeepy_test/handle/api_tests/test_api_close.py
@@ -14,6 +14,10 @@ if TYPE_CHECKING:
     from beekeepy.handle.runnable import Beekeeper
 
 
+# NOTE 1: Beekeeper should not raise exception while calling close on already closed wallet or not existing wallet.
+#         It should be treated as a success for UX purposes.
+
+
 def test_api_close(beekeeper: Beekeeper, wallet: WalletInfo) -> None:
     """Test test_api_close will test beekeeper_api.close api call."""
     # ARRANGE
@@ -51,8 +55,8 @@ def test_api_close_double_close(
     beekeeper.api.close(wallet_name=wallet.name)
 
     # ASSERT
-    with pytest.raises(ErrorInResponseError, match=f"Wallet not found: {wallet.name}"):
-        beekeeper.api.close(wallet_name=wallet.name)
+    # SHOULD NOT RAISE (NOTE 1)
+    beekeeper.api.close(wallet_name=wallet.name)
 
 
 def test_api_close_not_existing_wallet(beekeeper: Beekeeper) -> None:
@@ -61,5 +65,5 @@ def test_api_close_not_existing_wallet(beekeeper: Beekeeper) -> None:
     wallet = WalletInfo(password=generate_wallet_password(), name=generate_wallet_name())
 
     # ACT & ASSERT
-    with pytest.raises(ErrorInResponseError, match=f"Wallet not found: {wallet.name}"):
-        beekeeper.api.close(wallet_name=wallet.name)
+    # SHOULD NOT RAISE (NOTE 1)
+    beekeeper.api.close(wallet_name=wallet.name)
diff --git a/tests/beekeepy_test/handle/api_tests/test_api_close_session.py b/tests/beekeepy_test/handle/api_tests/test_api_close_session.py
index 81168d1f..a3a99f52 100644
--- a/tests/beekeepy_test/handle/api_tests/test_api_close_session.py
+++ b/tests/beekeepy_test/handle/api_tests/test_api_close_session.py
@@ -55,11 +55,7 @@ def test_api_close_session_not_existing(create_session: bool, beekeeper: Beekeep
     """Test test_api_close_session_not_existing will test possibility of closing not existing session."""
     # ARRANGE
     if create_session:
-        assert beekeeper.settings.notification_endpoint is not None
-        beekeeper.api.create_session(
-            notifications_endpoint=beekeeper.settings.notification_endpoint.as_string(with_protocol=False),
-            salt="salt",
-        )
+        beekeeper.api.create_session(salt="salt")
 
     # ACT & ASSERT
     beekeeper.set_session_token(WRONG_TOKEN)
diff --git a/tests/beekeepy_test/handle/api_tests/test_api_create_session.py b/tests/beekeepy_test/handle/api_tests/test_api_create_session.py
index 7201ab68..b206c0d0 100644
--- a/tests/beekeepy_test/handle/api_tests/test_api_create_session.py
+++ b/tests/beekeepy_test/handle/api_tests/test_api_create_session.py
@@ -14,17 +14,11 @@ if TYPE_CHECKING:
 
 def create_session(beekeeper: Beekeeper, salt: str) -> None:
     # ARRANGE
-    assert beekeeper.settings.notification_endpoint is not None
-    notification_endpoint = beekeeper.settings.notification_endpoint.as_string(with_protocol=False)
-    message_to_check = (
-        '"id":0,"jsonrpc":"2.0","method":"beekeeper_api.create_session",'
-        f'"params":{{"notifications_endpoint":"{notification_endpoint}","salt":"{salt}"}}'
-    )
+    message_to_check = '"id":0,"jsonrpc":"2.0","method":"beekeeper_api.create_session",' f'"params":{{"salt":"{salt}"}}'
 
     # ACT
     token = (
         beekeeper.api.create_session(
-            notifications_endpoint=notification_endpoint,
             salt=salt,
         )
     ).token
diff --git a/tests/beekeepy_test/handle/api_tests/test_api_set_timeout.py b/tests/beekeepy_test/handle/api_tests/test_api_set_timeout.py
index aba784b3..65f678b8 100644
--- a/tests/beekeepy_test/handle/api_tests/test_api_set_timeout.py
+++ b/tests/beekeepy_test/handle/api_tests/test_api_set_timeout.py
@@ -12,7 +12,7 @@ if TYPE_CHECKING:
 def test_api_set_timeout(beekeeper: Beekeeper, wallet: WalletInfo) -> None:  # noqa: ARG001
     """Test test_api_set_timeout will test beekeeper_api.set_timeout api call."""
     # ARRANGE
-    bk_wallet = (beekeeper.api.list_wallets()).wallets[0]
+    bk_wallet = (beekeeper.api.list_created_wallets()).wallets[0]
     assert bk_wallet.unlocked is True, "Wallet should be unlocked."
 
     # ACT
@@ -20,5 +20,5 @@ def test_api_set_timeout(beekeeper: Beekeeper, wallet: WalletInfo) -> None:  # n
     time.sleep(1.5)
 
     # ASSERT
-    bk_wallet = (beekeeper.api.list_wallets()).wallets[0]
+    bk_wallet = (beekeeper.api.list_created_wallets()).wallets[0]
     assert bk_wallet.unlocked is False, "Wallet after timeout should be locked."
diff --git a/tests/beekeepy_test/handle/basic/test_wallet.py b/tests/beekeepy_test/handle/basic/test_wallet.py
index 56253a75..ef1eac19 100644
--- a/tests/beekeepy_test/handle/basic/test_wallet.py
+++ b/tests/beekeepy_test/handle/basic/test_wallet.py
@@ -73,13 +73,13 @@ def test_timeout(beekeeper: Beekeeper, wallet: WalletInfo) -> None:
     # ASSERT
     info = beekeeper.api.get_info()
     assert timeout - (info.timeout_time - info.now).total_seconds() <= comparison_error_max_delta
-    check_wallets(beekeeper.api.list_wallets(), [wallet.name])
+    check_wallets(beekeeper.api.list_created_wallets(), [wallet.name])
 
     # ACT
     time.sleep(timeout + 1)
 
     # ASSERT
-    check_wallets(beekeeper.api.list_wallets(), [wallet.name], unlocked=False)
+    check_wallets(beekeeper.api.list_created_wallets(), [wallet.name], unlocked=False)
 
 
 @pytest.mark.parametrize("wallet_name", ["test", "123"])
diff --git a/tests/beekeepy_test/handle/commandline/application_command_line_options/patterns/config.ini b/tests/beekeepy_test/handle/commandline/application_command_line_options/patterns/config.ini
index 1b428e92..f55ef484 100644
--- a/tests/beekeepy_test/handle/commandline/application_command_line_options/patterns/config.ini
+++ b/tests/beekeepy_test/handle/commandline/application_command_line_options/patterns/config.ini
@@ -1,5 +1,5 @@
 # config automatically generated by helpy
-wallet-dir=/workspace/hive/tests/python/functional/beekeepy/handle/commandline/application_command_line_options/patterns
+wallet-dir=/workspace/helpy/tests/beekeepy_test/handle/commandline/application_command_line_options/patterns
 unlock-timeout=900
 unlock-interval=500
 webserver-http-endpoint=0.0.0.0:0
diff --git a/tests/beekeepy_test/handle/commandline/application_command_line_options/patterns/generate_help_pattern.py b/tests/beekeepy_test/handle/commandline/application_command_line_options/patterns/generate_help_pattern.py
index 67224e33..04a03f22 100644
--- a/tests/beekeepy_test/handle/commandline/application_command_line_options/patterns/generate_help_pattern.py
+++ b/tests/beekeepy_test/handle/commandline/application_command_line_options/patterns/generate_help_pattern.py
@@ -8,6 +8,9 @@ from beekeepy import Settings
 from beekeepy._executable.beekeeper_executable import BeekeeperExecutable
 
 if __name__ == "__main__":
-    help_text = BeekeeperExecutable(settings=Settings(), logger=loguru.logger).get_help_text()
+    settings = Settings()
+    help_text = BeekeeperExecutable(
+        executable_path=settings.binary_path, working_directory=settings.ensured_working_directory, logger=loguru.logger
+    ).get_help_text()
     help_pattern_file = Path.cwd() / "help_pattern.txt"
     help_pattern_file.write_text(help_text.strip())
diff --git a/tests/beekeepy_test/handle/commandline/application_command_line_options/patterns/help_pattern.txt b/tests/beekeepy_test/handle/commandline/application_command_line_options/patterns/help_pattern.txt
index 22331b24..b08615a8 100644
--- a/tests/beekeepy_test/handle/commandline/application_command_line_options/patterns/help_pattern.txt
+++ b/tests/beekeepy_test/handle/commandline/application_command_line_options/patterns/help_pattern.txt
@@ -27,9 +27,6 @@ Application Options:
           --export-keys-wallet "["green-wallet", 
           "PW5KYF9Rt4ETnuP4uheHSCm9kLbCuunf6RqeKgQ8QRoxZmGeZUhhk"]" 
 
-  --notifications-endpoint arg
-          list of addresses, that will receive notification about in-chain 
-          events
   --unlock-interval arg (=500)
           Protection against unlocking by bots. Every wrong `unlock` enables a 
           delay. By default 500[ms].
@@ -62,5 +59,4 @@ Application Command Line Options:
   -d [ --data-dir ] dir
           Directory containing configuration file config.ini. Default location: $HOME/.beekeeper or CWD/. beekeeper
   -c [ --config ] filename (="config.ini")
-          Configuration file name relative to data-dir
-
+          Configuration file name relative to data-dir
\ No newline at end of file
diff --git a/tests/beekeepy_test/handle/commandline/application_options/test_backtrace.py b/tests/beekeepy_test/handle/commandline/application_options/test_backtrace.py
index b843da39..8310070f 100644
--- a/tests/beekeepy_test/handle/commandline/application_options/test_backtrace.py
+++ b/tests/beekeepy_test/handle/commandline/application_options/test_backtrace.py
@@ -6,10 +6,10 @@ from typing import TYPE_CHECKING, Literal
 import pytest
 from local_tools.beekeepy import checkers
 
-from beekeepy._executable.arguments.beekeeper_arguments import BeekeeperArguments
+from beekeepy.handle.runnable import BeekeeperArguments
 
 if TYPE_CHECKING:
-    from beekeepy._executable.beekeeper_executable import (
+    from beekeepy.handle.runnable import (
         BeekeeperExecutable,
     )
 
@@ -19,9 +19,10 @@ def test_backtrace(backtrace: Literal["yes", "no"], beekeeper_exe: BeekeeperExec
     """Test will check command line flag --backtrace."""
     # ARRAGNE & ACT
 
-    with beekeeper_exe.run(
+    with beekeeper_exe.restore_arguments(
+        BeekeeperArguments(data_dir=beekeeper_exe.working_directory, backtrace=backtrace)
+    ), beekeeper_exe.run(
         blocking=False,
-        arguments=BeekeeperArguments(data_dir=beekeeper_exe.working_directory, backtrace=backtrace),
     ):
         time.sleep(0.1)
         # ASSERT
diff --git a/tests/beekeepy_test/handle/commandline/application_options/test_default_values.py b/tests/beekeepy_test/handle/commandline/application_options/test_default_values.py
index 4d7f9eac..53b4c75d 100644
--- a/tests/beekeepy_test/handle/commandline/application_options/test_default_values.py
+++ b/tests/beekeepy_test/handle/commandline/application_options/test_default_values.py
@@ -18,7 +18,6 @@ def check_default_values_from_config(default_config: BeekeeperConfig) -> None:
     assert default_config.log_json_rpc == BeekeeperDefaults.DEFAULT_LOG_JSON_RPC
     assert default_config.webserver_http_endpoint == http_webserver_default()
     assert default_config.webserver_thread_pool_size == BeekeeperDefaults.DEFAULT_WEBSERVER_THREAD_POOL_SIZE
-    assert default_config.notifications_endpoint == BeekeeperDefaults.DEFAULT_NOTIFICATIONS_ENDPOINT
     assert default_config.backtrace == BeekeeperDefaults.DEFAULT_BACKTRACE
     assert default_config.export_keys_wallet == BeekeeperDefaults.DEFAULT_EXPORT_KEYS_WALLET
 
diff --git a/tests/beekeepy_test/handle/commandline/application_options/test_export_keys_wallet.py b/tests/beekeepy_test/handle/commandline/application_options/test_export_keys_wallet.py
index b2cb2f02..b609ec5c 100644
--- a/tests/beekeepy_test/handle/commandline/application_options/test_export_keys_wallet.py
+++ b/tests/beekeepy_test/handle/commandline/application_options/test_export_keys_wallet.py
@@ -3,8 +3,8 @@ from __future__ import annotations
 import json
 from typing import TYPE_CHECKING, Final
 
-from beekeepy._interface.key_pair import KeyPair
 from beekeepy.handle.runnable import Beekeeper
+from beekeepy.interfaces import KeyPair
 
 if TYPE_CHECKING:
     from pathlib import Path
@@ -91,4 +91,4 @@ def test_export_keys(beekeeper: Beekeeper) -> None:
     # ASSERT
     # Check default path of wallet_name.keys
     check_dumped_keys(extract_path / wallet_name_keys, keys1)
-    assert bk.is_running is False
+    assert bk.is_running() is False
diff --git a/tests/beekeepy_test/handle/commandline/application_options/test_log_json_rpc.py b/tests/beekeepy_test/handle/commandline/application_options/test_log_json_rpc.py
index af22146a..1e48dbd8 100644
--- a/tests/beekeepy_test/handle/commandline/application_options/test_log_json_rpc.py
+++ b/tests/beekeepy_test/handle/commandline/application_options/test_log_json_rpc.py
@@ -2,7 +2,7 @@ from __future__ import annotations
 
 from typing import TYPE_CHECKING
 
-from beekeepy._executable.arguments.beekeeper_arguments import BeekeeperArguments
+from beekeepy.handle.runnable import BeekeeperArguments
 
 if TYPE_CHECKING:
     from pathlib import Path
diff --git a/tests/beekeepy_test/handle/commandline/application_options/test_notifications_endpoint.py b/tests/beekeepy_test/handle/commandline/application_options/test_notifications_endpoint.py
deleted file mode 100644
index 377251ac..00000000
--- a/tests/beekeepy_test/handle/commandline/application_options/test_notifications_endpoint.py
+++ /dev/null
@@ -1,37 +0,0 @@
-from __future__ import annotations
-
-from typing import TYPE_CHECKING
-
-import pytest
-
-from beekeepy.handle.runnable import BeekeeperArguments
-from beekeepy.interfaces import HttpUrl
-
-if TYPE_CHECKING:
-    from beekeepy.handle.runnable import Beekeeper
-
-
-@pytest.mark.parametrize(
-    "notifications_endpoint",
-    [HttpUrl("0.0.0.0:0", protocol="http"), HttpUrl("127.0.0.1:0", protocol="http")],
-)
-def test_notifications_endpoint(beekeeper_not_started: Beekeeper, notifications_endpoint: HttpUrl) -> None:
-    """
-    Test will check command line flag --notifications-endpoint.
-
-    In this test we will re-use built-in notification server. We will not pass a port here,
-    because built-in notification server will get free port and use it.
-
-    In order to test flag notifications-endpoint we will pass this flag, and internal
-    we will pass port that already has been taken by notification http server to beekeeper
-    executable.
-    """
-    # 1 pass notification flag
-    # 2 inside start function there is special if, that will check if we have explicitly pass
-    #   notification-endpoint flag, and append to it already taken port. This way we will
-    #   point beekeeper where to send notifications.
-
-    # ARRANGE & ACT & ASSERT
-    beekeeper_not_started.run(
-        additional_cli_arguments=BeekeeperArguments(notifications_endpoint=notifications_endpoint)
-    )
diff --git a/tests/beekeepy_test/handle/commandline/application_options/test_unlock_timeout.py b/tests/beekeepy_test/handle/commandline/application_options/test_unlock_timeout.py
index b04bb005..0ac7518a 100644
--- a/tests/beekeepy_test/handle/commandline/application_options/test_unlock_timeout.py
+++ b/tests/beekeepy_test/handle/commandline/application_options/test_unlock_timeout.py
@@ -5,7 +5,7 @@ from typing import TYPE_CHECKING
 
 import pytest
 
-from beekeepy._executable.arguments.beekeeper_arguments import BeekeeperArguments
+from beekeepy.handle.runnable import BeekeeperArguments
 
 if TYPE_CHECKING:
     from beekeepy.handle.runnable import Beekeeper
@@ -13,7 +13,7 @@ if TYPE_CHECKING:
 
 def check_wallet_lock(beekeeper: Beekeeper, required_status: bool) -> None:
     """Check if wallets are have required unlock status."""
-    response_list_wallets = beekeeper.api.list_wallets()
+    response_list_wallets = beekeeper.api.list_created_wallets()
     for wallet in response_list_wallets.wallets:
         assert wallet.unlocked == required_status
 
diff --git a/tests/beekeepy_test/handle/commandline/application_options/test_wallet_dir.py b/tests/beekeepy_test/handle/commandline/application_options/test_wallet_dir.py
index ec879fec..0a967be3 100644
--- a/tests/beekeepy_test/handle/commandline/application_options/test_wallet_dir.py
+++ b/tests/beekeepy_test/handle/commandline/application_options/test_wallet_dir.py
@@ -5,7 +5,7 @@ from typing import TYPE_CHECKING
 import pytest
 
 if TYPE_CHECKING:
-    from beekeepy._remote_handle.beekeeper import Beekeeper
+    from beekeepy.handle.remote import Beekeeper
 
 
 def check_wallets_size(beekeeper: Beekeeper, required_size: int) -> None:
diff --git a/tests/beekeepy_test/handle/commandline/application_options/test_webserver_http_endpoint.py b/tests/beekeepy_test/handle/commandline/application_options/test_webserver_http_endpoint.py
index 7a8e0ce1..ae56ed72 100644
--- a/tests/beekeepy_test/handle/commandline/application_options/test_webserver_http_endpoint.py
+++ b/tests/beekeepy_test/handle/commandline/application_options/test_webserver_http_endpoint.py
@@ -7,8 +7,8 @@ import pytest
 import requests
 from local_tools.beekeepy.network import get_port
 
-from beekeepy._executable.arguments.beekeeper_arguments import BeekeeperArguments
-from beekeepy._interface.url import HttpUrl
+from beekeepy.handle.runnable import BeekeeperArguments
+from beekeepy.interfaces import HttpUrl
 from schemas.apis import beekeeper_api
 from schemas.jsonrpc import get_response_model
 
@@ -16,15 +16,12 @@ if TYPE_CHECKING:
     from beekeepy.handle.runnable import Beekeeper
 
 
-def check_webserver_http_endpoint(*, nofification_endpoint: HttpUrl, webserver_http_endpoint: HttpUrl) -> None:
+def check_webserver_http_endpoint(*, webserver_http_endpoint: HttpUrl) -> None:
     """Check if beekeeper is listening on given endpoint."""
     data = {
         "jsonrpc": "2.0",
         "method": "beekeeper_api.create_session",
-        "params": {
-            "salt": "avocado",
-            "notifications_endpoint": nofification_endpoint.as_string(with_protocol=False),
-        },
+        "params": {"salt": "avocado"},
         "id": 1,
     }
 
@@ -49,8 +46,4 @@ def test_webserver_http_endpoint(beekeeper_not_started: Beekeeper, webserver_htt
     )
 
     # ASSERT
-    assert beekeeper_not_started.settings.notification_endpoint is not None
-    check_webserver_http_endpoint(
-        nofification_endpoint=beekeeper_not_started.settings.notification_endpoint,
-        webserver_http_endpoint=webserver_http_endpoint,
-    )
+    check_webserver_http_endpoint(webserver_http_endpoint=webserver_http_endpoint)
diff --git a/tests/beekeepy_test/handle/commandline/application_options/test_webserver_thread_pool_size.py b/tests/beekeepy_test/handle/commandline/application_options/test_webserver_thread_pool_size.py
index 38dbfb82..0f042945 100644
--- a/tests/beekeepy_test/handle/commandline/application_options/test_webserver_thread_pool_size.py
+++ b/tests/beekeepy_test/handle/commandline/application_options/test_webserver_thread_pool_size.py
@@ -5,7 +5,7 @@ from typing import TYPE_CHECKING
 import pytest
 from local_tools.beekeepy import checkers
 
-from beekeepy._executable.arguments.beekeeper_arguments import BeekeeperArguments
+from beekeepy.handle.runnable import BeekeeperArguments
 
 if TYPE_CHECKING:
     from beekeepy.handle.runnable import Beekeeper
diff --git a/tests/beekeepy_test/handle/commandline/conftest.py b/tests/beekeepy_test/handle/commandline/conftest.py
index 625860c4..c9d68fbe 100644
--- a/tests/beekeepy_test/handle/commandline/conftest.py
+++ b/tests/beekeepy_test/handle/commandline/conftest.py
@@ -13,4 +13,8 @@ if TYPE_CHECKING:
 @pytest.fixture()
 def beekeeper_exe(settings_with_logger: SettingsLoggerFactory) -> BeekeeperExecutable:
     incoming_settings, logger = settings_with_logger()
-    return BeekeeperExecutable(settings=incoming_settings, logger=logger)
+    return BeekeeperExecutable(
+        executable_path=incoming_settings.binary_path,
+        working_directory=incoming_settings.ensured_working_directory,
+        logger=logger,
+    )
diff --git a/tests/beekeepy_test/handle/conftest.py b/tests/beekeepy_test/handle/conftest.py
index 3063076f..434af5ff 100644
--- a/tests/beekeepy_test/handle/conftest.py
+++ b/tests/beekeepy_test/handle/conftest.py
@@ -27,8 +27,8 @@ def beekeeper_not_started(settings_with_logger: SettingsLoggerFactory) -> Iterat
 
     yield bk
 
-    if bk.is_running:
-        bk.teardown()
+    if bk.is_running():
+        bk.close()
 
 
 @pytest.fixture()
diff --git a/tests/beekeepy_test/handle/storage/test_storage.py b/tests/beekeepy_test/handle/storage/test_storage.py
index e98e83a7..b5712ba6 100644
--- a/tests/beekeepy_test/handle/storage/test_storage.py
+++ b/tests/beekeepy_test/handle/storage/test_storage.py
@@ -1,17 +1,16 @@
 from __future__ import annotations
 
-import json
 import shutil
-from pathlib import Path
+from typing import TYPE_CHECKING
 
-import pytest
 from local_tools.beekeepy import checkers
 from loguru import logger
 
 from beekeepy import Settings
-from beekeepy.exceptions import BeekeeperFailedToStartError
 from beekeepy.handle.runnable import Beekeeper
-from beekeepy.interfaces import HttpUrl
+
+if TYPE_CHECKING:
+    from pathlib import Path
 
 
 def prepare_directory(path: Path) -> None:
@@ -32,17 +31,13 @@ def test_multiply_beekeepeer_same_storage(working_directory: Path) -> None:
 
     # ACT & ASSERT 1
     with Beekeeper(settings=settings, logger=logger) as bk1:
-        assert bk1.is_running is True, "First instance of beekeeper should launch without any problems."
+        assert bk1.is_running() is True, "First instance of beekeeper should launch without any problems."
 
         # ACT & ASSERT 2
-        bk2 = Beekeeper(settings=settings, logger=logger)
-        with pytest.raises(BeekeeperFailedToStartError):
-            bk2.run()
-
-        assert checkers.check_for_pattern_in_file(
-            bk2.settings.ensured_working_directory / "stderr.log",
-            "Failed to lock access to wallet directory; is another `beekeeper` running?",
-        ), "There should be an info about another instance of beekeeper locking wallet directory."
+        with Beekeeper(settings=settings, logger=logger) as bk2:
+            assert (
+                "opening beekeeper failed" in bk2.apis.app_status.get_app_status().statuses
+            ), "Second instance of beekeeper should fail to start."
 
 
 def test_multiply_beekeepeer_different_storage(working_directory: Path) -> None:
@@ -63,8 +58,8 @@ def test_multiply_beekeepeer_different_storage(working_directory: Path) -> None:
         settings=Settings(working_directory=bk2_path), logger=logger
     ) as bk2:
         # ASSERT
-        assert bk1.is_running, "First instance of beekeeper should be working."
-        assert bk2.is_running, "Second instance of beekeeper should be working."
+        assert bk1.is_running(), "First instance of beekeeper should be working."
+        assert bk2.is_running(), "Second instance of beekeeper should be working."
         bks.extend((bk1, bk2))
 
     for bk in bks:
@@ -77,44 +72,12 @@ def test_multiply_beekeepeer_different_storage(working_directory: Path) -> None:
         ), "There should be an no info about another instance of beekeeper locking wallet directory."
 
 
-def get_remote_address_from_connection_file(working_dir: Path) -> HttpUrl:
-    connection: dict[str, str | int] = {}
-    with (working_dir / "beekeeper.connection").open() as file:
-        connection = json.load(file)
-    return HttpUrl(
-        f"{connection['address']}:{connection['port']}",
-        protocol=str(connection["type"]).lower(),  # type: ignore[arg-type]
-    )
-
-
 def test_beekeepers_files_generation(beekeeper: Beekeeper) -> None:
     """Test test_beekeepers_files_generation will check if beekeeper files are generated and have same content."""
     # ARRANGE & ACT
     wallet_dir = beekeeper.settings.ensured_working_directory
-    beekeeper_connection_file = wallet_dir / "beekeeper.connection"
-    beekeeper_pid_file = wallet_dir / "beekeeper.pid"
     beekeeper_wallet_lock_file = wallet_dir / "beekeeper.wallet.lock"
 
     # ASSERT
-    assert beekeeper_connection_file.exists() is True, "File 'beekeeper.connection' should exists"
-    assert beekeeper_pid_file.exists() is True, "File 'beekeeper.pid' should exists"
     # File beekeeper.wallet.lock holds no value inside, so we need only to check is its exists.
     assert beekeeper_wallet_lock_file.exists() is True, "File 'beekeeper.wallet.lock' should exists"
-
-    connection_url = get_remote_address_from_connection_file(wallet_dir)
-    assert connection_url is not None, "There should be connection details."
-
-    if beekeeper.http_endpoint.address == "127.0.0.1":
-        assert connection_url.address in [
-            "0.0.0.0",  # noqa: S104
-            "127.0.0.1",
-        ], "Address should point to localhost or all interfaces."
-    else:
-        assert connection_url.address == beekeeper.http_endpoint.address, "Host should be the same."
-    assert connection_url.port == beekeeper.http_endpoint.port, "Port should be the same."
-    assert connection_url.protocol == beekeeper.http_endpoint.protocol, "Protocol should be the same."
-
-    with Path.open(beekeeper_pid_file) as pid:
-        content = json.load(pid)
-
-        assert content["pid"] == str(beekeeper.pid), "Pid should be the same"
diff --git a/tests/beekeepy_test/handle/various/test_blocking_unlock.py b/tests/beekeepy_test/handle/various/test_blocking_unlock.py
index 8eb8edd3..a6961de8 100644
--- a/tests/beekeepy_test/handle/various/test_blocking_unlock.py
+++ b/tests/beekeepy_test/handle/various/test_blocking_unlock.py
@@ -8,12 +8,12 @@ from local_tools.beekeepy.generators import default_wallet_credentials
 from local_tools.beekeepy.models import SettingsLoggerFactory, WalletInfo
 from local_tools.beekeepy.network import async_raw_http_call
 
-from beekeepy._interface.delay_guard import DelayGuardBase
+from beekeepy._utilities.delay_guard import DelayGuardBase
 from beekeepy.handle.runnable import AsyncBeekeeper
 from schemas.jsonrpc import JSONRPCRequest
 
 if TYPE_CHECKING:
-    from beekeepy._interface.url import HttpUrl as Url
+    from beekeepy.interfaces import HttpUrl as Url
 
 
 # We have 500ms time period protection on ulocking wallet.
@@ -29,8 +29,8 @@ def beekeeper_not_started(settings_with_logger: SettingsLoggerFactory) -> Iterat
 
     yield bk
 
-    if bk.is_running:
-        bk.teardown()
+    if bk.is_running():
+        bk.close()
 
 
 @pytest.fixture()
@@ -98,14 +98,8 @@ async def test_wallet_blocking_timeout(beekeeper: AsyncBeekeeper, wallet: Wallet
     assert wallets[0].name == wallet.name
 
     unlock_jsons = []
-    assert beekeeper.settings.notification_endpoint is not None
     for i in range(5):
-        session = (
-            await beekeeper.api.create_session(
-                notifications_endpoint=beekeeper.settings.notification_endpoint.as_string(with_protocol=False),
-                salt=f"salt-{i}",
-            )
-        ).token
+        session = (await beekeeper.api.create_session(salt=f"salt-{i}")).token
         unlock_json = JSONRPCRequest(
             method="beekeeper_api.unlock",
             params={
diff --git a/tests/beekeepy_test/interface/test_setup.py b/tests/beekeepy_test/interface/test_setup.py
index 9680c4cb..a8607533 100644
--- a/tests/beekeepy_test/interface/test_setup.py
+++ b/tests/beekeepy_test/interface/test_setup.py
@@ -2,7 +2,10 @@ from __future__ import annotations
 
 from typing import TYPE_CHECKING
 
+import pytest
+
 from beekeepy import Beekeeper
+from beekeepy.exceptions import InvalidatedStateByClosingBeekeeperError, InvalidatedStateByClosingSessionError
 
 if TYPE_CHECKING:
     from local_tools.beekeepy.models import SettingsFactory
@@ -10,19 +13,20 @@ if TYPE_CHECKING:
 
 def test_closing_with_delete(settings: SettingsFactory) -> None:
     # ARRANGE
-    sets = settings()
-    bk = Beekeeper.factory(settings=sets)
+    bk = Beekeeper.factory(settings=settings())
 
     # ACT & ASSERT (no throw)
     bk.teardown()
-    assert not (sets.ensured_working_directory / "beekeeper.pid").exists()
+    with pytest.raises(InvalidatedStateByClosingBeekeeperError):
+        bk.create_session()
 
 
 def test_closing_with_with(settings: SettingsFactory) -> None:
     # ARRANGE, ACT & ASSERT (no throw)
-    sets = settings()
-    with Beekeeper.factory(settings=sets):
-        assert (sets.ensured_working_directory / "beekeeper.pid").exists()
+    with Beekeeper.factory(settings=settings()) as bk, bk.create_session() as session:
+        pass
+    with pytest.raises(InvalidatedStateByClosingSessionError):
+        session.wallets  # noqa: B018  # part of test
 
 
 def test_session_tokens(settings: SettingsFactory) -> None:
diff --git a/tests/beekeepy_test/interface/test_standalone_beekeeper.py b/tests/beekeepy_test/interface/test_standalone_beekeeper.py
index 47f5833d..265cc1eb 100644
--- a/tests/beekeepy_test/interface/test_standalone_beekeeper.py
+++ b/tests/beekeepy_test/interface/test_standalone_beekeeper.py
@@ -1,6 +1,5 @@
 from __future__ import annotations
 
-import json
 import os
 import sys
 import time
@@ -30,8 +29,7 @@ def verify_beekeeper_status(path_or_pid: Path | int, alive: bool) -> int:
     pid: int | None = None
     if isinstance(path_or_pid, Path):
         assert path_or_pid.exists(), f"Beekeeper started too slow, missing file: {path_or_pid.as_posix()}"
-        with path_or_pid.open("r") as rfile:
-            pid = int(json.load(rfile).get("pid", -1))
+        pid = int(path_or_pid.read_text().strip())
     else:
         pid = path_or_pid
 
@@ -45,7 +43,7 @@ def test_standalone_beekeeper(working_directory: Path) -> None:
     path_to_resource_directory = Path(__file__).resolve().parent / "resources"
     path_to_script = path_to_resource_directory / "standalone_beekeeper_by_args.py"
     path_to_working_directory = working_directory / "wdir"
-    path_to_pid_file = path_to_working_directory / "beekeeper.pid"
+    path_to_pid_file = path_to_working_directory / "pid.txt"
 
     # ACT & ASSERT
     run_python_script(
diff --git a/tests/conftest.py b/tests/conftest.py
index d0847cce..0e1c0b05 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -6,7 +6,7 @@ from pathlib import Path
 
 import pytest
 
-from beekeepy._remote_handle.abc.api import AbstractApi, RegisteredApisT
+from beekeepy.handle.remote import AbstractSyncApi, RegisteredApisT
 from beekeepy.interfaces import HttpUrl
 
 
@@ -30,12 +30,14 @@ def _convert_test_name_to_directory_name(test_name: str) -> str:
 @pytest.fixture(autouse=True)
 def working_directory(request: pytest.FixtureRequest) -> Path:
     name_of_directory = _convert_test_name_to_directory_name(request.node.name)
-    path_to_generated = request.node.path.parent / name_of_directory
-    if path_to_generated.exists():
-        shutil.rmtree(path_to_generated)
-    path_to_generated.mkdir()
-    assert isinstance(path_to_generated, Path), "given object is not Path"
-    return path_to_generated
+    path_to_module_generated = request.node.path.parent / f"generated_{request.node.path.stem}"
+    path_to_module_generated.mkdir(exist_ok=True)
+    path_to_test_artifacts = path_to_module_generated / name_of_directory
+    if path_to_test_artifacts.exists():
+        shutil.rmtree(path_to_test_artifacts)
+    path_to_test_artifacts.mkdir()
+    assert isinstance(path_to_test_artifacts, Path), "given object is not Path"
+    return path_to_test_artifacts
 
 
 def pytest_addoption(parser: pytest.Parser) -> None:
@@ -47,7 +49,7 @@ def pytest_addoption(parser: pytest.Parser) -> None:
 @pytest.fixture()
 def registered_apis() -> RegisteredApisT:
     """Return registered methods."""
-    return AbstractApi._get_registered_methods()
+    return AbstractSyncApi._get_registered_methods()
 
 
 @pytest.fixture()
diff --git a/tests/local-tools/local_tools/beekeepy/account_credentials.py b/tests/local-tools/local_tools/beekeepy/account_credentials.py
index a30be4bf..0f230121 100644
--- a/tests/local-tools/local_tools/beekeepy/account_credentials.py
+++ b/tests/local-tools/local_tools/beekeepy/account_credentials.py
@@ -3,7 +3,7 @@ from __future__ import annotations
 import random
 from typing import Final
 
-from beekeepy._interface.key_pair import KeyPair
+from beekeepy._executable import KeyPair
 
 ACCOUNTS_DATA: Final[list[dict[str, str]]] = [
     {
diff --git a/tests/local-tools/local_tools/beekeepy/network.py b/tests/local-tools/local_tools/beekeepy/network.py
index 2daa290b..d9de2944 100644
--- a/tests/local-tools/local_tools/beekeepy/network.py
+++ b/tests/local-tools/local_tools/beekeepy/network.py
@@ -4,18 +4,17 @@ import socket
 from json import loads
 from typing import TYPE_CHECKING, Any
 
-from beekeepy import Settings
-from beekeepy._communication.aiohttp_communicator import AioHttpCommunicator
-from beekeepy._communication.request_communicator import RequestCommunicator
+from beekeepy._communication import AioHttpCommunicator, RequestCommunicator
+from beekeepy._remote_handle import RemoteHandleSettings
 
 if TYPE_CHECKING:
-    from beekeepy._interface.url import HttpUrl
+    from beekeepy._communication import HttpUrl
     from schemas.jsonrpc import JSONRPCRequest
 
 
 async def async_raw_http_call(*, http_endpoint: HttpUrl, data: JSONRPCRequest) -> dict[str, Any]:
     """Make raw call with given data to given http_endpoint."""
-    communicator = AioHttpCommunicator(settings=Settings(http_endpoint=http_endpoint))
+    communicator = AioHttpCommunicator(settings=RemoteHandleSettings(http_endpoint=http_endpoint))
     response = await communicator.async_send(url=http_endpoint, data=data.json(by_alias=True))
     parsed = loads(response)
     assert isinstance(parsed, dict), "expected json object"
@@ -24,7 +23,7 @@ async def async_raw_http_call(*, http_endpoint: HttpUrl, data: JSONRPCRequest) -
 
 def raw_http_call(*, http_endpoint: HttpUrl, data: JSONRPCRequest) -> dict[str, Any]:
     """Make raw call with given data to given http_endpoint."""
-    communicator = RequestCommunicator(settings=Settings(http_endpoint=http_endpoint))
+    communicator = RequestCommunicator(settings=RemoteHandleSettings(http_endpoint=http_endpoint))
     response = communicator.send(url=http_endpoint, data=data.json(by_alias=True))
     parsed = loads(response)
     assert isinstance(parsed, dict), "expected json object"
diff --git a/tests/local-tools/poetry.lock b/tests/local-tools/poetry.lock
index f8973037..baa11fd2 100644
--- a/tests/local-tools/poetry.lock
+++ b/tests/local-tools/poetry.lock
@@ -175,10 +175,10 @@ develop = true
 aiohttp = "3.9.1"
 httpx = {version = "0.23.3", extras = ["http2"]}
 loguru = "0.7.2"
-pydantic = "1.10.18"
+psutil = "6.0.0"
 python-dateutil = "2.8.2"
 requests = "2.27.1"
-schemas = "0.0.1.dev323+e5a1ba1"
+schemas = "0.0.1.dev333+540050d"
 
 [package.source]
 type = "directory"
@@ -755,6 +755,35 @@ nodeenv = ">=0.11.1"
 pyyaml = ">=5.1"
 virtualenv = ">=20.10.0"
 
+[[package]]
+name = "psutil"
+version = "6.0.0"
+description = "Cross-platform lib for process and system monitoring in Python."
+optional = false
+python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7"
+files = [
+    {file = "psutil-6.0.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a021da3e881cd935e64a3d0a20983bda0bb4cf80e4f74fa9bfcb1bc5785360c6"},
+    {file = "psutil-6.0.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:1287c2b95f1c0a364d23bc6f2ea2365a8d4d9b726a3be7294296ff7ba97c17f0"},
+    {file = "psutil-6.0.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:a9a3dbfb4de4f18174528d87cc352d1f788b7496991cca33c6996f40c9e3c92c"},
+    {file = "psutil-6.0.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:6ec7588fb3ddaec7344a825afe298db83fe01bfaaab39155fa84cf1c0d6b13c3"},
+    {file = "psutil-6.0.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:1e7c870afcb7d91fdea2b37c24aeb08f98b6d67257a5cb0a8bc3ac68d0f1a68c"},
+    {file = "psutil-6.0.0-cp27-none-win32.whl", hash = "sha256:02b69001f44cc73c1c5279d02b30a817e339ceb258ad75997325e0e6169d8b35"},
+    {file = "psutil-6.0.0-cp27-none-win_amd64.whl", hash = "sha256:21f1fb635deccd510f69f485b87433460a603919b45e2a324ad65b0cc74f8fb1"},
+    {file = "psutil-6.0.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:c588a7e9b1173b6e866756dde596fd4cad94f9399daf99ad8c3258b3cb2b47a0"},
+    {file = "psutil-6.0.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ed2440ada7ef7d0d608f20ad89a04ec47d2d3ab7190896cd62ca5fc4fe08bf0"},
+    {file = "psutil-6.0.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5fd9a97c8e94059b0ef54a7d4baf13b405011176c3b6ff257c247cae0d560ecd"},
+    {file = "psutil-6.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2e8d0054fc88153ca0544f5c4d554d42e33df2e009c4ff42284ac9ebdef4132"},
+    {file = "psutil-6.0.0-cp36-cp36m-win32.whl", hash = "sha256:fc8c9510cde0146432bbdb433322861ee8c3efbf8589865c8bf8d21cb30c4d14"},
+    {file = "psutil-6.0.0-cp36-cp36m-win_amd64.whl", hash = "sha256:34859b8d8f423b86e4385ff3665d3f4d94be3cdf48221fbe476e883514fdb71c"},
+    {file = "psutil-6.0.0-cp37-abi3-win32.whl", hash = "sha256:a495580d6bae27291324fe60cea0b5a7c23fa36a7cd35035a16d93bdcf076b9d"},
+    {file = "psutil-6.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:33ea5e1c975250a720b3a6609c490db40dae5d83a4eb315170c4fe0d8b1f34b3"},
+    {file = "psutil-6.0.0-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:ffe7fc9b6b36beadc8c322f84e1caff51e8703b88eee1da46d1e3a6ae11b4fd0"},
+    {file = "psutil-6.0.0.tar.gz", hash = "sha256:8faae4f310b6d969fa26ca0545338b21f73c6b15db7c4a8d934a5482faa818f2"},
+]
+
+[package.extras]
+test = ["enum34", "ipaddress", "mock", "pywin32", "wmi"]
+
 [[package]]
 name = "pydantic"
 version = "1.10.18"
@@ -1017,12 +1046,12 @@ files = [
 
 [[package]]
 name = "schemas"
-version = "0.0.1.dev323+e5a1ba1"
+version = "0.0.1.dev333+540050d"
 description = "Tools for checking if message fits expected format"
 optional = false
 python-versions = ">=3.10,<4.0"
 files = [
-    {file = "schemas-0.0.1.dev323+e5a1ba1-py3-none-any.whl", hash = "sha256:b97546f24f54f58d71fade176e3fdfa8e113c040d35f56f04e5a567b0fa63d5f"},
+    {file = "schemas-0.0.1.dev333+540050d-py3-none-any.whl", hash = "sha256:0bdaab260640262a8b4b64e1a4c9fb7aefcedc0f418eb1a646fbad491a934ac7"},
 ]
 
 [package.dependencies]
@@ -1066,6 +1095,17 @@ files = [
     {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
 ]
 
+[[package]]
+name = "types-psutil"
+version = "6.0.0.20240901"
+description = "Typing stubs for psutil"
+optional = false
+python-versions = ">=3.8"
+files = [
+    {file = "types-psutil-6.0.0.20240901.tar.gz", hash = "sha256:437affa76670363db9ffecfa4f153cc6900bf8a7072b3420f3bc07a593f92226"},
+    {file = "types_psutil-6.0.0.20240901-py3-none-any.whl", hash = "sha256:20af311bfb0386a018a27ae47dc952119d7c0e849ff72b6aa24fc0433afb92a6"},
+]
+
 [[package]]
 name = "types-python-dateutil"
 version = "2.8.19.14"
@@ -1282,4 +1322,4 @@ multidict = ">=4.0"
 [metadata]
 lock-version = "2.0"
 python-versions = "^3.10"
-content-hash = "b04ca12a352bb62bdc19b6b7424482f8e49b950d17749b2b188ceecbcd5a47d5"
+content-hash = "ff055be77c71e2fbfc606e1993e4019e1a37b56e91fcd580c4934a7c2f9a9559"
diff --git a/tests/local-tools/pyproject.toml b/tests/local-tools/pyproject.toml
index 07b8d6d9..634ae01e 100644
--- a/tests/local-tools/pyproject.toml
+++ b/tests/local-tools/pyproject.toml
@@ -36,6 +36,7 @@ ruff = "0.4.9"
 types-python-dateutil = "2.8.19.14"
 types-pyyaml = "6.0.12.4"
 types-requests = "2.31.0.2"
+types-psutil = "6.0.0.20240901"
 
 
 [tool.mypy]
-- 
GitLab