diff --git a/src/Application.cpp b/src/Application.cpp
index bfa03bfe2..4b17def9e 100644
--- a/src/Application.cpp
+++ b/src/Application.cpp
@@ -79,6 +79,8 @@ void Application::initialize(Settings &settings, Paths &paths)
     assert(isAppInitialized == false);
     isAppInitialized = true;
 
+    Irc::getInstance().load();
+
     for (auto &singleton : this->singletons_)
     {
         singleton->initialize(settings, paths);
@@ -98,7 +100,6 @@ int Application::run(QApplication &qtApp)
     assert(isAppInitialized);
 
     this->twitch.server->connect();
-    Irc::getInstance().load();
 
     this->windows->getMainWindow().show();
 
diff --git a/src/common/Credentials.cpp b/src/common/Credentials.cpp
index fb5e5f983..37727de6d 100644
--- a/src/common/Credentials.cpp
+++ b/src/common/Credentials.cpp
@@ -86,6 +86,7 @@ Credentials::Credentials()
 }
 
 void Credentials::get(const QString &provider, const QString &name_,
+                      QObject *receiver,
                       std::function<void(const QString &)> &&onLoaded)
 {
     assertInGuiThread();
@@ -97,7 +98,7 @@ void Credentials::get(const QString &provider, const QString &name_,
         auto job = new QKeychain::ReadPasswordJob("chatterino");
         job->setAutoDelete(true);
         job->setKey(name);
-        QObject::connect(job, &QKeychain::Job::finished, qApp,
+        QObject::connect(job, &QKeychain::Job::finished, receiver,
                          [job, onLoaded = std::move(onLoaded)](auto) mutable {
                              onLoaded(job->textData());
                          },
diff --git a/src/common/Credentials.hpp b/src/common/Credentials.hpp
index d8dd9098b..b71ed6cd2 100644
--- a/src/common/Credentials.hpp
+++ b/src/common/Credentials.hpp
@@ -10,7 +10,7 @@ class Credentials
 public:
     static Credentials &getInstance();
 
-    void get(const QString &provider, const QString &name,
+    void get(const QString &provider, const QString &name, QObject *receiver,
              std::function<void(const QString &)> &&onLoaded);
     void set(const QString &provider, const QString &name,
              const QString &credential);
diff --git a/src/providers/irc/AbstractIrcServer.cpp b/src/providers/irc/AbstractIrcServer.cpp
index fa8de283b..31a7688f4 100644
--- a/src/providers/irc/AbstractIrcServer.cpp
+++ b/src/providers/irc/AbstractIrcServer.cpp
@@ -78,19 +78,26 @@ void AbstractIrcServer::connect()
 
     if (this->hasSeparateWriteConnection())
     {
-        this->initializeConnection(this->writeConnection_.get(), false, true);
-        this->initializeConnection(this->readConnection_.get(), true, false);
+        this->initializeConnection(this->writeConnection_.get(), Write);
+        this->initializeConnection(this->readConnection_.get(), Read);
     }
     else
     {
-        this->initializeConnection(this->readConnection_.get(), true, true);
+        this->initializeConnection(this->readConnection_.get(), Both);
     }
+}
 
-    // fourtf: this should be asynchronous
+void AbstractIrcServer::open(ConnectionType type)
+{
+    std::lock_guard<std::mutex> lock1(this->connectionMutex_);
+    std::lock_guard<std::mutex> lock2(this->channelMutex);
+
+    if (type == Write)
+    {
+        this->writeConnection_->open();
+    }
+    if (type & Read)
     {
-        std::lock_guard<std::mutex> lock1(this->connectionMutex_);
-        std::lock_guard<std::mutex> lock2(this->channelMutex);
-
         for (std::weak_ptr<Channel> &weak : this->channels.values())
         {
             if (auto channel = weak.lock())
@@ -98,11 +105,6 @@ void AbstractIrcServer::connect()
                 this->readConnection_->sendRaw("JOIN #" + channel->getName());
             }
         }
-
-        if (this->hasSeparateWriteConnection())
-        {
-            this->writeConnection_->open();
-        }
         this->readConnection_->open();
     }
 }
@@ -254,6 +256,16 @@ void AbstractIrcServer::onReadConnected(IrcConnection *connection)
 
     std::lock_guard lock(this->channelMutex);
 
+    // join channels
+    for (auto &&weak : this->channels)
+    {
+        if (auto channel = weak.lock())
+        {
+            connection->sendRaw("JOIN #" + channel->getName());
+        }
+    }
+
+    // connected/disconnected message
     auto connectedMsg = makeSystemMessage("connected");
     connectedMsg->flags.set(MessageFlag::ConnectedMessage);
     auto reconnected = makeSystemMessage("reconnected");
diff --git a/src/providers/irc/AbstractIrcServer.hpp b/src/providers/irc/AbstractIrcServer.hpp
index 23f22a407..5f1145519 100644
--- a/src/providers/irc/AbstractIrcServer.hpp
+++ b/src/providers/irc/AbstractIrcServer.hpp
@@ -17,6 +17,8 @@ using ChannelPtr = std::shared_ptr<Channel>;
 class AbstractIrcServer : public QObject
 {
 public:
+    enum ConnectionType { Read = 1, Write = 2, Both = 3 };
+
     virtual ~AbstractIrcServer() = default;
 
     // connection
@@ -43,8 +45,8 @@ public:
 protected:
     AbstractIrcServer();
 
-    virtual void initializeConnection(IrcConnection *connection, bool isRead,
-                                      bool isWrite) = 0;
+    virtual void initializeConnection(IrcConnection *connection,
+                                      ConnectionType type) = 0;
     virtual std::shared_ptr<Channel> createChannel(
         const QString &channelName) = 0;
 
@@ -63,6 +65,8 @@ protected:
     virtual bool hasSeparateWriteConnection() const = 0;
     virtual QString cleanChannelName(const QString &dirtyChannelName);
 
+    void open(ConnectionType type);
+
     QMap<QString, std::weak_ptr<Channel>> channels;
     std::mutex channelMutex;
 
diff --git a/src/providers/irc/Irc2.cpp b/src/providers/irc/Irc2.cpp
index 39e1bb9cc..9f0a746e9 100644
--- a/src/providers/irc/Irc2.cpp
+++ b/src/providers/irc/Irc2.cpp
@@ -38,7 +38,6 @@ namespace {
                 row[3]->data(Qt::EditRole).toString(),      // user
                 row[4]->data(Qt::EditRole).toString(),      // nick
                 row[5]->data(Qt::EditRole).toString(),      // real
-                original.password,                          // password
                 original.connectCommands,                   // connectCommands
                 original.id,                                // id
             };
@@ -70,6 +69,18 @@ inline QString getCredentialName(const IrcServerData &data)
            escape(data.host);
 }
 
+void IrcServerData::getPassword(
+    QObject *receiver, std::function<void(const QString &)> &&onLoaded) const
+{
+    Credentials::getInstance().get("irc", getCredentialName(*this), receiver,
+                                   std::move(onLoaded));
+}
+
+void IrcServerData::setPassword(const QString &password)
+{
+    Credentials::getInstance().set("irc", getCredentialName(*this), password);
+}
+
 Irc::Irc()
 {
     this->connections.itemInserted.connect([this](auto &&args) {
@@ -99,10 +110,6 @@ Irc::Irc()
             this->servers_.emplace(args.item.id,
                                    std::make_unique<IrcServer>(args.item));
         }
-
-        // store password
-        Credentials::getInstance().set("irc", getCredentialName(args.item),
-                                       args.item.password);
     });
 
     this->connections.itemRemoved.connect([this](auto &&args) {
@@ -217,6 +224,8 @@ void Irc::load()
     file.open(QIODevice::ReadOnly);
     auto object = QJsonDocument::fromJson(file.readAll()).object();
 
+    std::unordered_set<int> ids;
+
     // load servers
     for (auto server : object.value("servers").toArray())
     {
@@ -233,18 +242,11 @@ void Irc::load()
         data.id = obj.value("id").toInt(data.id);
 
         // duplicate id's are not allowed :(
-        if (this->abandonedChannels_.find(data.id) ==
-            this->abandonedChannels_.end())
+        if (ids.find(data.id) == ids.end())
         {
-            // insert element
-            this->abandonedChannels_[data.id];
+            ids.insert(data.id);
 
-            Credentials::getInstance().get(
-                "irc", getCredentialName(data),
-                [=](const QString &password) mutable {
-                    data.password = password;
-                    this->connections.appendItem(data);
-                });
+            this->connections.appendItem(data);
         }
     }
 }
diff --git a/src/providers/irc/Irc2.hpp b/src/providers/irc/Irc2.hpp
index a256f47d8..639c1bd2f 100644
--- a/src/providers/irc/Irc2.hpp
+++ b/src/providers/irc/Irc2.hpp
@@ -22,7 +22,10 @@ struct IrcServerData {
     QString real;
 
     //    IrcAuthType authType = Anonymous;
-    QString password;
+    void getPassword(QObject *receiver,
+                     std::function<void(const QString &)> &&onLoaded) const;
+    void setPassword(const QString &password);
+
     QStringList connectCommands;
 
     int id;
diff --git a/src/providers/irc/IrcChannel2.cpp b/src/providers/irc/IrcChannel2.cpp
index 6ccf681e1..d19af8b50 100644
--- a/src/providers/irc/IrcChannel2.cpp
+++ b/src/providers/irc/IrcChannel2.cpp
@@ -20,6 +20,7 @@ void IrcChannel::sendMessage(const QString &message)
         this->server()->sendMessage(this->getName(), message);
 
     MessageBuilder builder;
+    builder.emplace<TimestampElement>();
     builder.emplace<TextElement>(this->server()->nick() + ":",
                                  MessageElementFlag::Username);
     builder.emplace<TextElement>(message, MessageElementFlag::Text);
diff --git a/src/providers/irc/IrcServer.cpp b/src/providers/irc/IrcServer.cpp
index ac8e0f88f..9692698c1 100644
--- a/src/providers/irc/IrcServer.cpp
+++ b/src/providers/irc/IrcServer.cpp
@@ -5,6 +5,7 @@
 #include "messages/MessageBuilder.hpp"
 #include "providers/irc/Irc2.hpp"
 #include "providers/irc/IrcChannel2.hpp"
+#include "util/QObjectRef.hpp"
 
 namespace chatterino {
 
@@ -44,13 +45,13 @@ const QString &IrcServer::user()
 
 const QString &IrcServer::nick()
 {
-    return this->data_->nick;
+    return this->data_->nick.isEmpty() ? this->data_->user : this->data_->nick;
 }
 
-void IrcServer::initializeConnection(IrcConnection *connection, bool isRead,
-                                     bool isWrite)
+void IrcServer::initializeConnection(IrcConnection *connection,
+                                     ConnectionType type)
 {
-    assert(isRead && isWrite);
+    assert(type == Both);
 
     connection->setSecure(this->data_->ssl);
     connection->setHost(this->data_->host);
@@ -61,7 +62,18 @@ void IrcServer::initializeConnection(IrcConnection *connection, bool isRead,
                                                         : this->data_->nick);
     connection->setRealName(this->data_->real.isEmpty() ? this->data_->user
                                                         : this->data_->nick);
-    connection->setPassword(this->data_->password);
+
+    this->data_->getPassword(
+        this, [conn = new QObjectRef(connection) /* can't copy */,
+               this](const QString &password) mutable {
+            if (*conn)
+            {
+                (*conn)->setPassword(password);
+                this->open(Both);
+            }
+
+            delete conn;
+        });
 }
 
 std::shared_ptr<Channel> IrcServer::createChannel(const QString &channelName)
@@ -76,22 +88,16 @@ bool IrcServer::hasSeparateWriteConnection() const
 
 void IrcServer::onReadConnected(IrcConnection *connection)
 {
-    AbstractIrcServer::onReadConnected(connection);
-
-    std::lock_guard lock(this->channelMutex);
-
-    for (auto &&command : this->data_->connectCommands)
     {
-        connection->sendRaw(command + "\r\n");
-    }
+        std::lock_guard lock(this->channelMutex);
 
-    for (auto &&weak : this->channels)
-    {
-        if (auto channel = weak.lock())
+        for (auto &&command : this->data_->connectCommands)
         {
-            connection->sendRaw("JOIN #" + channel->getName());
+            connection->sendRaw(command + "\r\n");
         }
     }
+
+    AbstractIrcServer::onReadConnected(connection);
 }
 
 void IrcServer::privateMessageReceived(Communi::IrcPrivateMessage *message)
diff --git a/src/providers/irc/IrcServer.hpp b/src/providers/irc/IrcServer.hpp
index 71deb9081..94419f622 100644
--- a/src/providers/irc/IrcServer.hpp
+++ b/src/providers/irc/IrcServer.hpp
@@ -21,8 +21,8 @@ public:
 
     // AbstractIrcServer interface
 protected:
-    void initializeConnection(IrcConnection *connection, bool isRead,
-                              bool isWrite) override;
+    void initializeConnection(IrcConnection *connection,
+                              ConnectionType type) override;
     std::shared_ptr<Channel> createChannel(const QString &channelName) override;
     bool hasSeparateWriteConnection() const override;
 
diff --git a/src/providers/twitch/TwitchServer.cpp b/src/providers/twitch/TwitchServer.cpp
index fa1b158ac..ff6c1c9bb 100644
--- a/src/providers/twitch/TwitchServer.cpp
+++ b/src/providers/twitch/TwitchServer.cpp
@@ -63,11 +63,9 @@ void TwitchServer::initialize(Settings &settings, Paths &paths)
     this->ffz.loadEmotes();
 }
 
-void TwitchServer::initializeConnection(IrcConnection *connection, bool isRead,
-                                        bool isWrite)
+void TwitchServer::initializeConnection(IrcConnection *connection,
+                                        ConnectionType type)
 {
-    this->singleConnection_ = isRead == isWrite;
-
     std::shared_ptr<TwitchAccount> account =
         getApp()->accounts->twitch.getCurrent();
 
@@ -97,6 +95,8 @@ void TwitchServer::initializeConnection(IrcConnection *connection, bool isRead,
     // SSL enabled: irc://irc.chat.twitch.tv:6697
     connection->setHost("irc.chat.twitch.tv");
     connection->setPort(6697);
+
+    this->open(type);
 }
 
 std::shared_ptr<Channel> TwitchServer::createChannel(const QString &channelName)
diff --git a/src/providers/twitch/TwitchServer.hpp b/src/providers/twitch/TwitchServer.hpp
index e0adcf6ef..c14347f4a 100644
--- a/src/providers/twitch/TwitchServer.hpp
+++ b/src/providers/twitch/TwitchServer.hpp
@@ -44,8 +44,8 @@ public:
     const FfzEmotes &getFfzEmotes() const;
 
 protected:
-    virtual void initializeConnection(IrcConnection *connection, bool isRead,
-                                      bool isWrite) override;
+    virtual void initializeConnection(IrcConnection *connection,
+                                      ConnectionType type) override;
     virtual std::shared_ptr<Channel> createChannel(
         const QString &channelName) override;
 
@@ -75,7 +75,6 @@ private:
     std::chrono::steady_clock::time_point lastErrorTimeSpeed_;
     std::chrono::steady_clock::time_point lastErrorTimeAmount_;
 
-    bool singleConnection_ = false;
     TwitchBadges twitchBadges;
     BttvEmotes bttv;
     FfzEmotes ffz;
diff --git a/src/util/QObjectRef.hpp b/src/util/QObjectRef.hpp
index cf8ad69ee..4a15545d1 100644
--- a/src/util/QObjectRef.hpp
+++ b/src/util/QObjectRef.hpp
@@ -62,8 +62,9 @@ private:
         if (other)
         {
             this->conn_ =
-                QObject::connect(other, &QObject::destroyed,
-                                 [this](QObject *) { this->set(nullptr); });
+                QObject::connect(other, &QObject::destroyed, qApp,
+                                 [this](QObject *) { this->set(nullptr); },
+                                 Qt::DirectConnection);
         }
 
         this->t_ = other;
diff --git a/src/widgets/dialogs/IrcConnectionEditor.cpp b/src/widgets/dialogs/IrcConnectionEditor.cpp
index dfa13dad8..53f2cd19f 100644
--- a/src/widgets/dialogs/IrcConnectionEditor.cpp
+++ b/src/widgets/dialogs/IrcConnectionEditor.cpp
@@ -28,7 +28,10 @@ IrcConnectionEditor::IrcConnectionEditor(const IrcServerData &data, bool isAdd,
     this->ui_->realNameLineEdit->setText(data.real);
     this->ui_->connectCommandsEditor->setPlainText(
         data.connectCommands.join('\n'));
-    this->ui_->passwordLineEdit->setText(data.password);
+
+    data.getPassword(this, [this](const QString &password) {
+        this->ui_->passwordLineEdit->setText(password);
+    });
 
     QFont font("Monospace");
     font.setStyleHint(QFont::TypeWriter);
@@ -51,7 +54,7 @@ IrcServerData IrcConnectionEditor::data()
     data.real = this->ui_->realNameLineEdit->text();
     data.connectCommands =
         this->ui_->connectCommandsEditor->toPlainText().split('\n');
-    data.password = this->ui_->passwordLineEdit->text();
+    data.setPassword(this->ui_->passwordLineEdit->text());
     return data;
 }
 
diff --git a/src/widgets/dialogs/IrcConnectionEditor.ui b/src/widgets/dialogs/IrcConnectionEditor.ui
index df79f6d31..db1c6124f 100644
--- a/src/widgets/dialogs/IrcConnectionEditor.ui
+++ b/src/widgets/dialogs/IrcConnectionEditor.ui
@@ -158,7 +158,11 @@
      <item row="10" column="0">
       <widget class="QLabel" name="connectCommandsLabel">
        <property name="text">
-        <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Send IRC commands&lt;/p&gt;&lt;p&gt;on connect:&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+        <string>Send IRC commands
+on connect:</string>
+       </property>
+       <property name="textFormat">
+        <enum>Qt::PlainText</enum>
        </property>
       </widget>
      </item>