diff --git a/CMakeLists.txt b/CMakeLists.txt
index 44e59d3..939e3bc 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -4,7 +4,7 @@ project(jay
     LANGUAGES CXX
 )
 
-set(CMAKE_CXX_STANDARD 20)
+set(CMAKE_CXX_STANDARD 23)
 set(CMAKE_CXX_STANDARD_REQUIRED ON)
 
 cmake_policy(SET CMP0076 NEW)
diff --git a/component/CMakeLists.txt b/component/CMakeLists.txt
index 7e77ed0..ba46907 100644
--- a/component/CMakeLists.txt
+++ b/component/CMakeLists.txt
@@ -1,6 +1,5 @@
 set(SOURCES
     config.cpp
-    logger.cpp
     actor.cpp
     router.cpp
     core.cpp
@@ -8,7 +7,6 @@ set(SOURCES
 
 set(HEADERS
     config.h
-    logger.h
     actor.h
     router.h
     core.h
diff --git a/component/config.cpp b/component/config.cpp
index d3838e5..418544d 100644
--- a/component/config.cpp
+++ b/component/config.cpp
@@ -3,7 +3,8 @@
 
 #include "config.h"
 
-Config::Config(const std::string& path):
+Config::Config(const std::filesystem::path& path):
+    path(path),
     root(YAML::LoadFile(path))
 {}
 
@@ -23,21 +24,21 @@ std::string Config::getFullJID() const {
     return getBareJID() + "/" + getResource();
 }
 
-Logger::Level Config::getLogLevel() const {
+Shared::Logger::Level Config::getLogLevel() const {
     std::string level = root["logLevel"].as<std::string>("info");
 
     if (level == "trace")
-        return Logger::trace;
+        return Shared::Logger::trace;
     else if (level == "debug")
-        return Logger::debug;
+        return Shared::Logger::debug;
     else if (level == "info")
-        return Logger::info;
+        return Shared::Logger::info;
     else if (level == "warn" || level == "warning")
-        return Logger::warning;
+        return Shared::Logger::warning;
     else if (level == "error")
-        return Logger::error;
+        return Shared::Logger::error;
     else
-        return Logger::info;
+        return Shared::Logger::info;
 }
 
 gloox::TLSPolicy Config::getTLSPolicy() const {
@@ -115,3 +116,14 @@ Shared::Responses Config::getResponses() const {
 
     return responses;
 }
+
+void Config::setActors(const std::map<std::string, std::string>& actors) {
+    YAML::Node cfg = root["actors"] = YAML::Node(YAML::NodeType::Map);
+
+    for (const std::pair<const std::string, std::string>& pair : actors)
+        cfg[pair.first] = pair.second;
+
+    std::ofstream out(path);
+    out << YAML::Dump(root);
+    out.close();
+}
diff --git a/component/config.h b/component/config.h
index 9169159..86138c9 100644
--- a/component/config.h
+++ b/component/config.h
@@ -5,6 +5,8 @@
 
 #include <string>
 #include <map>
+#include <fstream>
+#include <filesystem>
 
 #include "gloox/gloox.h"
 
@@ -12,7 +14,7 @@
 
 #include "shared/definitions.h"
 #include "shared/result.h"
-#include "logger.h"
+#include "shared/logger.h"
 
 class Config {
 public:
@@ -23,7 +25,7 @@ public:
     };
 
 public:
-    Config(const std::string& path);
+    Config(const std::filesystem::path& path);
 
     bool isValid() const;
 
@@ -32,11 +34,14 @@ public:
     std::string getPassword() const;
     std::string getResource() const;
     std::map<std::string, std::string> getActors() const;
-    Logger::Level getLogLevel() const;
+    Shared::Logger::Level getLogLevel() const;
     gloox::TLSPolicy getTLSPolicy() const;
     Module getModuleConfig(const std::string& name) const;
     Shared::Responses getResponses() const;
 
+    void setActors(const std::map<std::string, std::string>& actors);
+
 private:
+    std::filesystem::path path;
     YAML::Node root;
 };
diff --git a/component/core.cpp b/component/core.cpp
index ad89f2d..ae2b3bd 100644
--- a/component/core.cpp
+++ b/component/core.cpp
@@ -15,7 +15,7 @@ Core::Core(const std::string& configPath):
 void Core::send(const std::string& jid, const std::string& body) {
     std::shared_ptr<Connection> cn = connection.lock();
     if (!cn) {
-        logger.log(Logger::warning, "Couldn't send a message to " + jid + ", connection is not available", {"Core"});
+        logger.log(Shared::Logger::warning, "Couldn't send a message to " + jid + ", connection is not available", {"Core"});
         return;
     }
 
@@ -37,12 +37,15 @@ void Core::initialize(const std::shared_ptr<Connection>& cn) {
 
 void Core::setGroup(const std::string& jid, const std::string& group) {
     router.registerActor(jid, group);
-    // todo save config
+
+    std::map<std::string, std::string> actors = router.getActors();
+    actors["default"] = router.getDefaultGroup();
+    config.setActors(actors);
 }
 
 void Core::initializeActors() {
     for (const std::pair<const std::string, std::string>& pair : config.getActors()) {
-        logger.log(Logger::info, "registering actor " + pair.first + " as " + pair.second, {"Core"});
+        logger.log(Shared::Logger::info, "registering actor " + pair.first + " as " + pair.second, {"Core"});
         router.registerActor(pair.first, pair.second);
     }
 }
diff --git a/component/core.h b/component/core.h
index 6a63a66..3272694 100644
--- a/component/core.h
+++ b/component/core.h
@@ -6,9 +6,9 @@
 #include <string>
 #include <memory>
 
+#include "shared/logger.h"
 #include "config.h"
 #include "router.h"
-#include "logger.h"
 
 class Connection;
 
@@ -22,7 +22,7 @@ public:
 
 public:
     Config config;
-    Logger logger;
+    Shared::Logger logger;
     Router router;
 
 private:
diff --git a/component/router.cpp b/component/router.cpp
index b6314a9..2da0a58 100644
--- a/component/router.cpp
+++ b/component/router.cpp
@@ -6,8 +6,8 @@
 #include "module/module.h"
 #include "connection/connection.h"
 
-Router::Router(const Logger& logger):
-    logger(logger),
+Router::Router(const Shared::Logger& logger):
+    Shared::Loggable(logger, {"Router"}),
     modules(),
     actors(),
     defaultGroup("Stranger"),
@@ -44,13 +44,13 @@ void Router::routeMessage(const std::string& sender, const std::string& body) {
     std::vector<std::string> args = Module::Module::split(body);
     Modules::iterator mItr = modules.find(args[0]);
     if (mItr == modules.end()) {
-        logger.log(Logger::debug, "could not find module \"" + args[0] + "\" to handle message from " + sender, {"Router"});
+        debug("could not find module \"" + args[0] + "\" to handle message from " + sender);
         return onMessageResult(Shared::unhandled, sender);
     }
 
     std::shared_ptr<Module::Module> module = mItr->second.lock();
     if (!module) {
-        logger.log(Logger::error, "could not lock module \"" + mItr->first + "\" to handle message from " + sender, {"Router"});
+        error("could not lock module \"" + mItr->first + "\" to handle message from " + sender);
         return onMessageResult(Shared::error, sender);
     }
 
@@ -59,9 +59,9 @@ void Router::routeMessage(const std::string& sender, const std::string& body) {
     try {
         result = module->message(aItr->second, args);
         if (result == Shared::success)
-        logger.log(Logger::debug, "module \"" + mItr->first + "\" successfully handled message from " + sender, {"Router"});
+        debug("module \"" + mItr->first + "\" successfully handled message from " + sender);
     } catch (...) {
-        logger.log(Logger::error, "module \"" + mItr->first + "\" thew an unhandled exception handling message from " + sender, {"Router"});
+        error("module \"" + mItr->first + "\" thew an unhandled exception handling message from " + sender);
         result = Shared::error;
     }
 
@@ -77,6 +77,10 @@ std::map<std::string, std::string> Router::getActors() const {
     return result;
 }
 
+std::string Router::getDefaultGroup() const {
+    return defaultGroup;
+}
+
 void Router::setConnection(const std::shared_ptr<Connection>& cn) {
     connection = cn;
 }
@@ -100,7 +104,7 @@ void Router::onMessageResult(Shared::Result result, const std::string& sender) {
     
     std::shared_ptr<Connection> cn = connection.lock();
     if (!cn) {
-        logger.log(Logger::warning, "Couldn't send a message to " + sender + ", connection is not available", {"Router"});
+        warn("Couldn't send a message to " + sender + ", connection is not available");
         return;
     }
 
diff --git a/component/router.h b/component/router.h
index 4d9caeb..bacb578 100644
--- a/component/router.h
+++ b/component/router.h
@@ -10,8 +10,8 @@
 #include <random>
 
 #include "shared/result.h"
+#include "shared/loggable.h"
 #include "actor.h"
-#include "logger.h"
 
 namespace Module {
     class Module;
@@ -19,15 +19,16 @@ namespace Module {
 
 class Connection;
 
-class Router {
+class Router : private Shared::Loggable {
 public:
-    Router(const Logger& logger);
+    Router(const Shared::Logger& logger);
 
     void registerModule(const std::string& key, const std::shared_ptr<Module::Module>& module);
     void registerActor(const std::string& key, const std::string& group);
     void routeMessage(const std::string& sender, const std::string& body);
 
     std::map<std::string, std::string> getActors() const;
+    std::string getDefaultGroup() const;
 
     void setConnection(const std::shared_ptr<Connection>& connection);
     void setResponses(Shared::Result result, const Shared::Strings& options);
@@ -43,7 +44,6 @@ private:
     typedef std::map<Shared::Result, List> Responses;
 
     std::weak_ptr<Connection> connection;
-    const Logger& logger;
     Modules modules;
     Actors actors;
     std::string defaultGroup;
diff --git a/connection/connection.cpp b/connection/connection.cpp
index ce78083..e3b2519 100644
--- a/connection/connection.cpp
+++ b/connection/connection.cpp
@@ -4,6 +4,7 @@
 #include "connection.h"
 
 Connection::Connection(const std::shared_ptr<Core>& core):
+    Shared::Loggable(core->logger, {"Connection"}),
     state(initial),
     core(core),
     gloox()
@@ -21,7 +22,7 @@ void Connection::initialize() {
     gloox->registerConnectionListener(this);
     gloox->registerMessageHandler(this);
 
-    ::gloox::LogLevel level = Logger::convert(core->config.getLogLevel());
+    ::gloox::LogLevel level = Shared::Logger::convert(core->config.getLogLevel());
     gloox->logInstance().registerLogHandler(level, gloox::LogAreaAll, &core->logger);
 
     gloox::Disco* disco = gloox->disco();
@@ -39,7 +40,7 @@ void Connection::deinitialize() {
     if (state == initial)
         return;
 
-    core->logger.log(Logger::debug, "deinitializing", {"Connection"});
+    debug("deinitializing");
     gloox->logInstance().removeLogHandler(&core->logger);
 
     gloox->removeMessageHandler(this);
@@ -53,14 +54,14 @@ void Connection::connect() {
     if (state != disconnected)
         return;
 
-    core->logger.log(Logger::debug, "connecting", {"Connection"});
+    debug("connecting");
     state = connected;
     gloox->connect(true);
     state = disconnected;
 }
 
 void Connection::send(const std::string& jid, const std::string& body) {
-    core->logger.log(Logger::debug, "sending message \"" + body + "\" to " + jid, {"Connection"});
+    debug("sending message \"" + body + "\" to " + jid);
     gloox->send(gloox::Message(gloox::Message::Chat, jid, body));
 }
 
@@ -73,12 +74,12 @@ void Connection::handleMessage(const gloox::Message& message, gloox::MessageSess
         return;
 
     std::string jid = message.from().bare();
-    core->logger.log(Logger::debug, "received message \"" + body + "\" from " + jid, {"Connection"});
+    debug("received message \"" + body + "\" from " + jid);
     core->router.routeMessage(jid, body);
 }
 
 void Connection::onConnect() {
-    core->logger.log(Logger::info, "connection established", {"Connection"});
+    info("connection established");
 }
 void Connection::onDisconnect(gloox::ConnectionError e) {
     std::string error;
@@ -143,12 +144,12 @@ void Connection::onDisconnect(gloox::ConnectionError e) {
     }
 
     if (error.empty())
-        core->logger.log(Logger::info, "disconnected"  , {"Connection"});
+        info("disconnected");
     else
-        core->logger.log(Logger::error, "disconnected: " + error  , {"Connection"});
+        Loggable::error("disconnected: " + error);
 }
 bool Connection::onTLSConnect(const gloox::CertInfo&) {
-    core->logger.log(Logger::info, "TLS established", {"Connection"});
+    info("TLS established");
     return true;
 }
 
diff --git a/connection/connection.h b/connection/connection.h
index 910635c..c079ddb 100644
--- a/connection/connection.h
+++ b/connection/connection.h
@@ -11,9 +11,11 @@
 #include <gloox/connectionlistener.h>
 #include <gloox/messagehandler.h>
 
+#include "shared/loggable.h"
 #include "component/core.h"
 
 class Connection:
+    private Shared::Loggable,
     public gloox::ConnectionListener,
     public gloox::MessageHandler
 {
diff --git a/jay.cpp b/jay.cpp
index ade530d..7c47a7a 100644
--- a/jay.cpp
+++ b/jay.cpp
@@ -51,7 +51,7 @@ void Jay::createModules() {
         if (!conf.enabled)
             continue;
 
-        core->logger.log(Logger::info, "enabling module " + pair.first, {"Jay"});
+        core->logger.log(Shared::Logger::info, "enabling module " + pair.first, {"Jay"});
         modules.emplace_back(pair.second(core, conf.permissions));
         core->router.registerModule(pair.first, modules.back());
     }
diff --git a/module/actor.cpp b/module/actor.cpp
index ee68076..22d3427 100644
--- a/module/actor.cpp
+++ b/module/actor.cpp
@@ -4,7 +4,7 @@
 #include "actor.h"
 
 Module::Actor::Actor(const std::shared_ptr<Core>& core, const Shared::Permissions& permissions):
-    Module(core, permissions)
+    Module(core, permissions, "Actor")
 {}
 
 Module::Actor::~Actor() noexcept {}
@@ -51,7 +51,7 @@ std::string Module::Actor::list() {
 }
 
 std::string Module::Actor::set(const std::string& jid, const std::string& group) {
-    core->setGroup(jid, group);
+    core->setGroup(lower(jid), group);
 
     return jid + " is now " + core->router.getGroup(jid);
 }
diff --git a/module/module.cpp b/module/module.cpp
index 3865188..7c4f47f 100644
--- a/module/module.cpp
+++ b/module/module.cpp
@@ -7,7 +7,8 @@
 
 #include "gloox/message.h"
 
-Module::Module::Module(const std::shared_ptr<Core>& core, const Shared::Permissions& permissions):
+Module::Module::Module(const std::shared_ptr<Core>& core, const Shared::Permissions& permissions, const std::string& name):
+    Shared::Loggable(core->logger, {"Module", name}),
     core(core),
     permissions(permissions)
 {}
@@ -36,3 +37,7 @@ std::vector<std::string> Module::Module::split(const std::string& string, const
 
     return result;
 }
+
+std::string Module::Module::lower(const std::string& text) {
+    return std::ranges::to<std::string>(text | std::views::transform(::tolower));
+}
diff --git a/module/module.h b/module/module.h
index b8f2513..2881a5e 100644
--- a/module/module.h
+++ b/module/module.h
@@ -6,17 +6,20 @@
 #include <string>
 #include <memory>
 #include <vector>
+#include <ranges>
+#include <cctype>
 
 #include "shared/definitions.h"
 #include "shared/result.h"
+#include "shared/loggable.h"
 #include "component/core.h"
 #include "component/actor.h"
 
 namespace Module {
 
-class Module {
+class Module : protected Shared::Loggable {
 protected:
-    Module(const std::shared_ptr<Core>& core, const Shared::Permissions& permissions);
+    Module(const std::shared_ptr<Core>& core, const Shared::Permissions& permissions, const std::string& name);
 
     bool hasPermission(const std::string& permission, const std::shared_ptr<::Actor>& actor) const;
 
@@ -24,6 +27,7 @@ public:
     virtual ~Module() noexcept;
 
     static Shared::Strings split(const std::string& string, const std::string& delimiter = " ");
+    static std::string lower(const std::string& text);
 
     virtual Shared::Result message(const std::shared_ptr<::Actor>& actor, const Shared::Strings& args) = 0;
 
diff --git a/shared/CMakeLists.txt b/shared/CMakeLists.txt
index d157eb4..d08bcb0 100644
--- a/shared/CMakeLists.txt
+++ b/shared/CMakeLists.txt
@@ -1,7 +1,11 @@
 set(SOURCES
+    logger.cpp
+    loggable.cpp
 )
 
 set(HEADERS
+    logger.h
+    loggable.h
     definitions.h
     result.h
 )
diff --git a/shared/loggable.cpp b/shared/loggable.cpp
new file mode 100644
index 0000000..52dbcf0
--- /dev/null
+++ b/shared/loggable.cpp
@@ -0,0 +1,29 @@
+// SPDX-FileCopyrightText: 2024 Yury Gubich <blue@macaw.me>
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "loggable.h"
+
+Shared::Loggable::Loggable(const Logger& logger, const std::vector<std::string>& domain) :
+    logger(logger),
+    domain(domain)
+{}
+
+void Shared::Loggable::trace(const std::string& message) const {
+    logger.log(Logger::trace, message, domain);
+}
+
+void Shared::Loggable::debug(const std::string& message) const {
+    logger.log(Logger::debug, message, domain);
+}
+
+void Shared::Loggable::info(const std::string& message) const {
+    logger.log(Logger::info, message, domain);
+}
+
+void Shared::Loggable::warn(const std::string& message) const {
+    logger.log(Logger::warning, message, domain);
+}
+
+void Shared::Loggable::error(const std::string& message) const {
+    logger.log(Logger::error, message, domain);
+}
diff --git a/shared/loggable.h b/shared/loggable.h
new file mode 100644
index 0000000..48d9eb7
--- /dev/null
+++ b/shared/loggable.h
@@ -0,0 +1,29 @@
+// SPDX-FileCopyrightText: 2024 Yury Gubich <blue@macaw.me>
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include <string>
+#include <vector>
+
+#include "logger.h"
+
+namespace Shared {
+
+class Loggable {
+public:
+    Loggable(const Logger& logger, const std::vector<std::string>& domain = {"Unknown"});
+    
+private:
+    const Logger& logger;
+    std::vector<std::string> domain;
+
+protected:
+    void trace(const std::string& message) const;
+    void debug(const std::string& message) const;
+    void info(const std::string& message) const;
+    void warn(const std::string& message) const;
+    void error(const std::string& message) const;
+};
+
+}
\ No newline at end of file
diff --git a/component/logger.cpp b/shared/logger.cpp
similarity index 87%
rename from component/logger.cpp
rename to shared/logger.cpp
index 0e7f10b..fb8ddab 100644
--- a/component/logger.cpp
+++ b/shared/logger.cpp
@@ -39,11 +39,11 @@ void writeTimestamp() {
 }
 
 constexpr std::string_view getLogLevel(gloox::LogLevel level) {
-    return (level >= 0 && level < 3) ? logLevelMap[Logger::convert(level)] : "UNKNOWN";
+    return (level >= 0 && level < 3) ? logLevelMap[Shared::Logger::convert(level)] : "UNKNOWN";
 }
 
 constexpr std::string_view getColor(gloox::LogLevel level) {
-    return (level >= 0 && level < 3) ? colorMap[Logger::convert(level)] : "";
+    return (level >= 0 && level < 3) ? colorMap[Shared::Logger::convert(level)] : "";
 }
 
 void writeTags(gloox::LogArea area) {
@@ -84,7 +84,7 @@ void writeTags(gloox::LogArea area) {
         std::cout << '\t';
 }
 
-Logger::Level Logger::convert(gloox::LogLevel level) {
+Shared::Logger::Level Shared::Logger::convert(gloox::LogLevel level) {
     switch (level) {
         case gloox::LogLevelDebug:
             return trace;
@@ -97,7 +97,7 @@ Logger::Level Logger::convert(gloox::LogLevel level) {
     return warning;
 }
 
-gloox::LogLevel Logger::convert(Level level) {
+gloox::LogLevel Shared::Logger::convert(Level level) {
     switch (level) {
         case trace:
             return gloox::LogLevelDebug;
@@ -116,20 +116,20 @@ gloox::LogLevel Logger::convert(Level level) {
     return gloox::LogLevelWarning;
 }
 
-Logger::Logger(Level level):
+Shared::Logger::Logger(Level level):
     level(level)
 {}
 
-Logger::~Logger() {}
+Shared::Logger::~Logger() {}
 
-void Logger::handleLog(gloox::LogLevel level, gloox::LogArea area, const std::string& message) {
+void Shared::Logger::handleLog(gloox::LogLevel level, gloox::LogArea area, const std::string& message) {
     writeTimestamp();
     std::cout << getColor(level) << bold << '[' << getLogLevel(level) << ']' << clear << bold;
     writeTags(area);
     std::cout << clear << '\t' << message << clear << std::endl;
 }
 
-void Logger::log(Level lvl, const std::string& message, const std::vector<std::string>& domain) const {
+void Shared::Logger::log(Level lvl, const std::string& message, const std::vector<std::string>& domain) const {
     if (lvl < level)
         return;
 
diff --git a/component/logger.h b/shared/logger.h
similarity index 97%
rename from component/logger.h
rename to shared/logger.h
index 6059745..f1f08e5 100644
--- a/component/logger.h
+++ b/shared/logger.h
@@ -8,6 +8,8 @@
 #include <gloox/loghandler.h>
 #include <gloox/logsink.h>
 
+namespace Shared {
+
 class Logger: public gloox::LogHandler {
 public:
     enum Level {
@@ -33,3 +35,5 @@ public:
 private:
     Level level;
 };
+
+}
\ No newline at end of file