diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp
index aedf451924..31cab68cdf 100644
--- a/assignment-client/src/octree/OctreeServer.cpp
+++ b/assignment-client/src/octree/OctreeServer.cpp
@@ -236,6 +236,10 @@ OctreeServer::OctreeServer(ReceivedMessage& message) :
 {
     _averageLoopTime.updateAverage(0);
     qDebug() << "Octree server starting... [" << this << "]";
+
+    // make sure the AccountManager has an Auth URL for payment redemptions
+
+    AccountManager::getInstance().setAuthURL(NetworkingConstants::METAVERSE_SERVER_URL);
 }
 
 OctreeServer::~OctreeServer() {
diff --git a/domain-server/resources/web/settings/js/settings.js b/domain-server/resources/web/settings/js/settings.js
index fae07ace45..7dc94421be 100644
--- a/domain-server/resources/web/settings/js/settings.js
+++ b/domain-server/resources/web/settings/js/settings.js
@@ -778,7 +778,7 @@ function chooseFromHighFidelityDomains(clickedButton) {
 function createTemporaryDomain() {
   swal({
     title: 'Create temporary place name',
-    text: "This will create a temporary place name and domain ID"
+    text: "This will create a temporary place name and domain ID (valid for 30 days)"
       + " so other users can easily connect to your domain.</br></br>"
       + "In order to make your domain reachable, this will also enable full automatic networking.",
     showCancelButton: true,
diff --git a/domain-server/src/DomainGatekeeper.cpp b/domain-server/src/DomainGatekeeper.cpp
index f385f5c489..3e4ee7b758 100644
--- a/domain-server/src/DomainGatekeeper.cpp
+++ b/domain-server/src/DomainGatekeeper.cpp
@@ -331,6 +331,7 @@ bool DomainGatekeeper::verifyUserSignature(const QString& username,
                                                                 QCryptographicHash::Sha256);
         
         if (rsaPublicKey) {
+            QByteArray decryptedArray(RSA_size(rsaPublicKey), 0);
             int decryptResult = RSA_verify(NID_sha256,
                                            reinterpret_cast<const unsigned char*>(usernameWithToken.constData()),
                                            usernameWithToken.size(),
diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp
index f0df67a6f6..9e13c8e6fa 100644
--- a/domain-server/src/DomainServer.cpp
+++ b/domain-server/src/DomainServer.cpp
@@ -96,7 +96,7 @@ DomainServer::DomainServer(int argc, char* argv[]) :
     // make sure we hear about newly connected nodes from our gatekeeper
     connect(&_gatekeeper, &DomainGatekeeper::connectedNode, this, &DomainServer::handleConnectedNode);
 
-    if (optionallyReadX509KeyAndCertificate() && optionallySetupOAuth()) {
+    if (optionallyReadX509KeyAndCertificate() && optionallySetupOAuth() && optionallySetupAssignmentPayment()) {
         // we either read a certificate and private key or were not passed one
         // and completed login or did not need to
 
@@ -198,6 +198,7 @@ bool DomainServer::optionallySetupOAuth() {
     }
 
     AccountManager& accountManager = AccountManager::getInstance();
+    accountManager.disableSettingsFilePersistence();
     accountManager.setAuthURL(_oauthProviderURL);
 
     _oauthClientID = settingsMap.value(OAUTH_CLIENT_ID_OPTION).toString();
@@ -371,12 +372,20 @@ void DomainServer::setupNodeListAndAssignments(const QUuid& sessionUUID) {
     packetReceiver.registerListener(PacketType::ICEPing, &_gatekeeper, "processICEPingPacket");
     packetReceiver.registerListener(PacketType::ICEPingReply, &_gatekeeper, "processICEPingReplyPacket");
     packetReceiver.registerListener(PacketType::ICEServerPeerInformation, &_gatekeeper, "processICEPeerInformationPacket");
-    packetReceiver.registerListener(PacketType::ICEServerHeartbeatDenied, this, "processICEServerHeartbeatDenialPacket");
     
     // add whatever static assignments that have been parsed to the queue
     addStaticAssignmentsToQueue();
 }
 
+bool DomainServer::didSetupAccountManagerWithAccessToken() {
+    if (AccountManager::getInstance().hasValidAccessToken()) {
+        // we already gave the account manager a valid access token
+        return true;
+    }
+
+    return resetAccountManagerAccessToken();
+}
+
 const QString ACCESS_TOKEN_KEY_PATH = "metaverse.access_token";
 
 bool DomainServer::resetAccountManagerAccessToken() {
@@ -392,13 +401,9 @@ bool DomainServer::resetAccountManagerAccessToken() {
             if (accessTokenVariant && accessTokenVariant->canConvert(QMetaType::QString)) {
                 accessToken = accessTokenVariant->toString();
             } else {
-                qDebug() << "A domain-server feature that requires authentication is enabled but no access token is present.";
-                qDebug() << "Set an access token via the web interface, in your user or master config"
+                qDebug() << "A domain-server feature that requires authentication is enabled but no access token is present."
+                    << "Set an access token via the web interface, in your user or master config"
                     << "at keypath metaverse.access_token or in your ENV at key DOMAIN_SERVER_ACCESS_TOKEN";
-
-                // clear any existing access token from AccountManager
-                AccountManager::getInstance().setAccessTokenForCurrentAuthURL(QString());
-
                 return false;
             }
         } else {
@@ -424,6 +429,34 @@ bool DomainServer::resetAccountManagerAccessToken() {
     }
 }
 
+bool DomainServer::optionallySetupAssignmentPayment() {
+    const QString PAY_FOR_ASSIGNMENTS_OPTION = "pay-for-assignments";
+    const QVariantMap& settingsMap = _settingsManager.getSettingsMap();
+
+    if (settingsMap.contains(PAY_FOR_ASSIGNMENTS_OPTION) &&
+        settingsMap.value(PAY_FOR_ASSIGNMENTS_OPTION).toBool() &&
+        didSetupAccountManagerWithAccessToken()) {
+
+        qDebug() << "Assignments will be paid for via" << qPrintable(_oauthProviderURL.toString());
+
+        // assume that the fact we are authing against HF data server means we will pay for assignments
+        // setup a timer to send transactions to pay assigned nodes every 30 seconds
+        QTimer* creditSetupTimer = new QTimer(this);
+        connect(creditSetupTimer, &QTimer::timeout, this, &DomainServer::setupPendingAssignmentCredits);
+
+        const qint64 CREDIT_CHECK_INTERVAL_MSECS = 5 * 1000;
+        creditSetupTimer->start(CREDIT_CHECK_INTERVAL_MSECS);
+
+        QTimer* nodePaymentTimer = new QTimer(this);
+        connect(nodePaymentTimer, &QTimer::timeout, this, &DomainServer::sendPendingTransactionsToServer);
+
+        const qint64 TRANSACTION_SEND_INTERVAL_MSECS = 30 * 1000;
+        nodePaymentTimer->start(TRANSACTION_SEND_INTERVAL_MSECS);
+    }
+
+    return true;
+}
+
 void DomainServer::setupAutomaticNetworking() {
     auto nodeList = DependencyManager::get<LimitedNodeList>();
 
@@ -434,9 +467,9 @@ void DomainServer::setupAutomaticNetworking() {
         setupICEHeartbeatForFullNetworking();
     }
 
-    if (!resetAccountManagerAccessToken()) {
-        qDebug() << "Will not send heartbeat to Metaverse API without an access token.";
-        qDebug() << "If this is not a temporary domain add an access token to your config file or via the web interface.";
+    if (!didSetupAccountManagerWithAccessToken()) {
+        qDebug() << "Cannot send heartbeat to data server without an access token.";
+        qDebug() << "Add an access token to your config file or via the web interface.";
 
         return;
     }
@@ -493,19 +526,6 @@ void DomainServer::setupICEHeartbeatForFullNetworking() {
     // we need this DS to know what our public IP is - start trying to figure that out now
     limitedNodeList->startSTUNPublicSocketUpdate();
 
-    // to send ICE heartbeats we'd better have a private key locally with an uploaded public key
-    auto& accountManager = AccountManager::getInstance();
-    auto domainID = accountManager.getAccountInfo().getDomainID();
-
-    // if we have an access token and we don't have a private key or the current domain ID has changed
-    // we should generate a new keypair
-    if (!accountManager.getAccountInfo().hasPrivateKey() || domainID != limitedNodeList->getSessionUUID()) {
-        accountManager.generateNewDomainKeypair(limitedNodeList->getSessionUUID());
-    }
-
-    // hookup to the signal from account manager that tells us when keypair is available
-    connect(&accountManager, &AccountManager::newKeypair, this, &DomainServer::handleKeypairChange);
-
     if (!_iceHeartbeatTimer) {
         // setup a timer to heartbeat with the ice-server every so often
         _iceHeartbeatTimer = new QTimer { this };
@@ -1062,76 +1082,11 @@ void DomainServer::sendHeartbeatToDataServer(const QString& networkAddress) {
                                               domainUpdateJSON.toUtf8());
 }
 
+// TODO: have data-web respond with ice-server hostname to use
+
 void DomainServer::sendHeartbeatToIceServer() {
     if (!_iceServerSocket.getAddress().isNull()) {
-
-        auto& accountManager = AccountManager::getInstance();
-        auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
-
-        if (!accountManager.getAccountInfo().hasPrivateKey()) {
-            qWarning() << "Cannot send an ice-server heartbeat without a private key for signature.";
-            qWarning() << "Waiting for keypair generation to complete before sending ICE heartbeat.";
-
-            if (!limitedNodeList->getSessionUUID().isNull()) {
-                accountManager.generateNewDomainKeypair(limitedNodeList->getSessionUUID());
-            } else {
-                qWarning() << "Attempting to send ICE server heartbeat with no domain ID. This is not supported";
-            }
-
-            return;
-        }
-
-        // NOTE: I'd love to specify the correct size for the packet here, but it's a little trickey with
-        // QDataStream and the possibility of IPv6 address for the sockets.
-        if (!_iceServerHeartbeatPacket) {
-            _iceServerHeartbeatPacket = NLPacket::create(PacketType::ICEServerHeartbeat);
-        }
-
-        bool shouldRecreatePacket = false;
-
-        if (_iceServerHeartbeatPacket->getPayloadSize() > 0) {
-            // if either of our sockets have changed we need to re-sign the heartbeat
-            // first read the sockets out from the current packet
-            _iceServerHeartbeatPacket->seek(0);
-            QDataStream heartbeatStream(_iceServerHeartbeatPacket.get());
-
-            QUuid senderUUID;
-            HifiSockAddr publicSocket, localSocket;
-            heartbeatStream >> senderUUID >> publicSocket >> localSocket;
-
-            if (senderUUID != limitedNodeList->getSessionUUID()
-                || publicSocket != limitedNodeList->getPublicSockAddr()
-                || localSocket != limitedNodeList->getLocalSockAddr()) {
-                shouldRecreatePacket = true;
-            }
-        } else {
-            shouldRecreatePacket = true;
-        }
-
-        if (shouldRecreatePacket) {
-            // either we don't have a heartbeat packet yet or some combination of sockets, ID and keypair have changed
-            // and we need to make a new one
-
-            // reset the position in the packet before writing
-            _iceServerHeartbeatPacket->reset();
-
-            // write our plaintext data to the packet
-            QDataStream heartbeatDataStream(_iceServerHeartbeatPacket.get());
-            heartbeatDataStream << limitedNodeList->getSessionUUID()
-                << limitedNodeList->getPublicSockAddr() << limitedNodeList->getLocalSockAddr();
-
-            // setup a QByteArray that points to the plaintext data
-            auto plaintext = QByteArray::fromRawData(_iceServerHeartbeatPacket->getPayload(), _iceServerHeartbeatPacket->getPayloadSize());
-
-            // generate a signature for the plaintext data in the packet
-            auto signature = accountManager.getAccountInfo().signPlaintext(plaintext);
-
-            // pack the signature with the data
-            heartbeatDataStream << signature;
-        }
-
-        // send the heartbeat packet to the ice server now
-        limitedNodeList->sendUnreliablePacket(*_iceServerHeartbeatPacket, _iceServerSocket);
+        DependencyManager::get<LimitedNodeList>()->sendHeartbeatToIceServer(_iceServerSocket);
     }
 }
 
@@ -2015,31 +1970,3 @@ void DomainServer::processNodeDisconnectRequestPacket(QSharedPointer<ReceivedMes
         });
     }
 }
-
-void DomainServer::processICEServerHeartbeatDenialPacket(QSharedPointer<ReceivedMessage> message) {
-    static const int NUM_HEARTBEAT_DENIALS_FOR_KEYPAIR_REGEN = 3;
-
-    static int numHeartbeatDenials = 0;
-    if (++numHeartbeatDenials > NUM_HEARTBEAT_DENIALS_FOR_KEYPAIR_REGEN) {
-        qDebug() << "Received" << NUM_HEARTBEAT_DENIALS_FOR_KEYPAIR_REGEN << "heartbeat denials from ice-server"
-            << "- re-generating keypair now";
-
-        // we've hit our threshold of heartbeat denials, trigger a keypair re-generation
-        auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
-        AccountManager::getInstance().generateNewDomainKeypair(limitedNodeList->getSessionUUID());
-
-        // reset our number of heartbeat denials
-        numHeartbeatDenials = 0;
-    }
-}
-
-void DomainServer::handleKeypairChange() {
-    if (_iceServerHeartbeatPacket) {
-        // reset the payload size of the ice-server heartbeat packet - this causes the packet to be re-generated
-        // the next time we go to send an ice-server heartbeat
-        _iceServerHeartbeatPacket->setPayloadSize(0);
-
-        // send a heartbeat to the ice server immediately
-        sendHeartbeatToIceServer();
-    }
-}
diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h
index 3a83e8696b..326ca3e1a8 100644
--- a/domain-server/src/DomainServer.h
+++ b/domain-server/src/DomainServer.h
@@ -61,7 +61,6 @@ public slots:
     void processNodeJSONStatsPacket(QSharedPointer<ReceivedMessage> packetList, SharedNodePointer sendingNode);
     void processPathQueryPacket(QSharedPointer<ReceivedMessage> packet);
     void processNodeDisconnectRequestPacket(QSharedPointer<ReceivedMessage> message);
-    void processICEServerHeartbeatDenialPacket(QSharedPointer<ReceivedMessage> message);
     
 private slots:
     void aboutToQuit();
@@ -79,16 +78,16 @@ private slots:
     void handleTempDomainError(QNetworkReply& requestReply);
 
     void queuedQuit(QString quitMessage, int exitCode);
-
-    void handleKeypairChange();
     
 private:
     void setupNodeListAndAssignments(const QUuid& sessionUUID = QUuid::createUuid());
     bool optionallySetupOAuth();
     bool optionallyReadX509KeyAndCertificate();
+    bool optionallySetupAssignmentPayment();
 
     void optionallyGetTemporaryName(const QStringList& arguments);
 
+    bool didSetupAccountManagerWithAccessToken();
     bool resetAccountManagerAccessToken();
 
     void setupAutomaticNetworking();
@@ -154,7 +153,6 @@ private:
     DomainServerSettingsManager _settingsManager;
 
     HifiSockAddr _iceServerSocket;
-    std::unique_ptr<NLPacket> _iceServerHeartbeatPacket;
 
     QTimer* _iceHeartbeatTimer { nullptr }; // this looks like it dangles when created but it's parented to the DomainServer
     
diff --git a/ice-server/CMakeLists.txt b/ice-server/CMakeLists.txt
index e5bdffe2e2..cfec3c966c 100644
--- a/ice-server/CMakeLists.txt
+++ b/ice-server/CMakeLists.txt
@@ -6,17 +6,3 @@ setup_hifi_project(Network)
 # link the shared hifi libraries
 link_hifi_libraries(embedded-webserver networking shared)
 package_libraries_for_deployment()
-
-# find OpenSSL
-find_package(OpenSSL REQUIRED)
-
-if (APPLE AND ${OPENSSL_INCLUDE_DIR} STREQUAL "/usr/include")
-  # this is a user on OS X using system OpenSSL, which is going to throw warnings since they're deprecating for their common crypto
-  message(WARNING "The found version of OpenSSL is the OS X system version. This will produce deprecation warnings."
-    "\nWe recommend you install a newer version (at least 1.0.1h) in a different directory and set OPENSSL_ROOT_DIR in your env so Cmake can find it.")
-endif ()
-
-include_directories(SYSTEM "${OPENSSL_INCLUDE_DIR}")
-
-# append OpenSSL to our list of libraries to link
-target_link_libraries(${TARGET_NAME} ${OPENSSL_LIBRARIES})
diff --git a/ice-server/src/IceServer.cpp b/ice-server/src/IceServer.cpp
index f38923b873..2baa7a13a7 100644
--- a/ice-server/src/IceServer.cpp
+++ b/ice-server/src/IceServer.cpp
@@ -9,21 +9,14 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
-#include "IceServer.h"
-
-#include <openssl/rsa.h>
-#include <openssl/x509.h>
-
-#include <QtCore/QJsonDocument>
-#include <QtCore/QTimer>
-#include <QtNetwork/QNetworkReply>
-#include <QtNetwork/QNetworkRequest>
+#include <QTimer>
 
 #include <LimitedNodeList.h>
-#include <NetworkingConstants.h>
 #include <udt/PacketHeaders.h>
 #include <SharedUtil.h>
 
+#include "IceServer.h"
+
 const int CLEAR_INACTIVE_PEERS_INTERVAL_MSECS = 1 * 1000;
 const int PEER_SILENCE_THRESHOLD_MSECS = 5 * 1000;
 
@@ -52,6 +45,7 @@ IceServer::IceServer(int argc, char* argv[]) :
     QTimer* inactivePeerTimer = new QTimer(this);
     connect(inactivePeerTimer, &QTimer::timeout, this, &IceServer::clearInactivePeers);
     inactivePeerTimer->start(CLEAR_INACTIVE_PEERS_INTERVAL_MSECS);
+
 }
 
 bool IceServer::packetVersionMatch(const udt::Packet& packet) {
@@ -76,14 +70,9 @@ void IceServer::processPacket(std::unique_ptr<udt::Packet> packet) {
         
         if (nlPacket->getType() == PacketType::ICEServerHeartbeat) {
             SharedNetworkPeer peer = addOrUpdateHeartbeatingPeer(*nlPacket);
-            if (peer) {
-                // so that we can send packets to the heartbeating peer when we need, we need to activate a socket now
-                peer->activateMatchingOrNewSymmetricSocket(nlPacket->getSenderSockAddr());
-            } else {
-                // we couldn't verify this peer - respond back to them so they know they may need to perform keypair re-generation
-                static auto deniedPacket = NLPacket::create(PacketType::ICEServerHeartbeatDenied);
-                _serverSocket.writePacket(*deniedPacket, nlPacket->getSenderSockAddr());
-            }
+            
+            // so that we can send packets to the heartbeating peer when we need, we need to activate a socket now
+            peer->activateMatchingOrNewSymmetricSocket(nlPacket->getSenderSockAddr());
         } else if (nlPacket->getType() == PacketType::ICEServerQuery) {
             QDataStream heartbeatStream(nlPacket.get());
             
@@ -125,135 +114,31 @@ SharedNetworkPeer IceServer::addOrUpdateHeartbeatingPeer(NLPacket& packet) {
     // pull the UUID, public and private sock addrs for this peer
     QUuid senderUUID;
     HifiSockAddr publicSocket, localSocket;
-    QByteArray signature;
 
     QDataStream heartbeatStream(&packet);
-    heartbeatStream >> senderUUID >> publicSocket >> localSocket;
+    
+    heartbeatStream >> senderUUID;
+    heartbeatStream >> publicSocket >> localSocket;
 
-    auto signedPlaintext = QByteArray::fromRawData(packet.getPayload(), heartbeatStream.device()->pos());
-    heartbeatStream >> signature;
+    // make sure we have this sender in our peer hash
+    SharedNetworkPeer matchingPeer = _activePeers.value(senderUUID);
 
-    // make sure this is a verified heartbeat before performing any more processing
-    if (isVerifiedHeartbeat(senderUUID, signedPlaintext, signature)) {
-        // make sure we have this sender in our peer hash
-        SharedNetworkPeer matchingPeer = _activePeers.value(senderUUID);
+    if (!matchingPeer) {
+        // if we don't have this sender we need to create them now
+        matchingPeer = QSharedPointer<NetworkPeer>::create(senderUUID, publicSocket, localSocket);
+        _activePeers.insert(senderUUID, matchingPeer);
 
-        if (!matchingPeer) {
-            // if we don't have this sender we need to create them now
-            matchingPeer = QSharedPointer<NetworkPeer>::create(senderUUID, publicSocket, localSocket);
-            _activePeers.insert(senderUUID, matchingPeer);
-
-            qDebug() << "Added a new network peer" << *matchingPeer;
-        } else {
-            // we already had the peer so just potentially update their sockets
-            matchingPeer->setPublicSocket(publicSocket);
-            matchingPeer->setLocalSocket(localSocket);
-        }
-
-        // update our last heard microstamp for this network peer to now
-        matchingPeer->setLastHeardMicrostamp(usecTimestampNow());
-        
-        return matchingPeer;
+        qDebug() << "Added a new network peer" << *matchingPeer;
     } else {
-        // not verified, return the empty peer object
-        return SharedNetworkPeer();
-    }
-}
-
-bool IceServer::isVerifiedHeartbeat(const QUuid& domainID, const QByteArray& plaintext, const QByteArray& signature) {
-    // check if we have a private key for this domain ID - if we do not then fire off the request for it
-    auto it = _domainPublicKeys.find(domainID);
-    if (it != _domainPublicKeys.end()) {
-
-        // attempt to verify the signature for this heartbeat
-        const unsigned char* publicKeyData = reinterpret_cast<const unsigned char*>(it->second.constData());
-
-        // first load up the public key into an RSA struct
-        RSA* rsaPublicKey = d2i_RSA_PUBKEY(NULL, &publicKeyData, it->second.size());
-
-        if (rsaPublicKey) {
-            auto hashedPlaintext = QCryptographicHash::hash(plaintext, QCryptographicHash::Sha256);
-            int verificationResult = RSA_verify(NID_sha256,
-                                                reinterpret_cast<const unsigned char*>(hashedPlaintext.constData()),
-                                                hashedPlaintext.size(),
-                                                reinterpret_cast<const unsigned char*>(signature.constData()),
-                                                signature.size(),
-                                                rsaPublicKey);
-
-            // free up the public key and remove connection token before we return
-            RSA_free(rsaPublicKey);
-
-            if (verificationResult == 1) {
-                // this is the only success case - we return true here to indicate that the heartbeat is verified
-                return true;
-            } else {
-                qDebug() << "Failed to verify heartbeat for" << domainID << "- re-requesting public key from API.";
-            }
-
-        } else {
-            // we can't let this user in since we couldn't convert their public key to an RSA key we could use
-            qWarning() << "Could not convert in-memory public key for" << domainID << "to usable RSA public key.";
-            qWarning() << "Re-requesting public key from API";
-        }
+        // we already had the peer so just potentially update their sockets
+        matchingPeer->setPublicSocket(publicSocket);
+        matchingPeer->setLocalSocket(localSocket);
     }
 
-    // we could not verify this heartbeat (missing public key, could not load public key, bad actor)
-    // ask the metaverse API for the right public key and return false to indicate that this is not verified
-    requestDomainPublicKey(domainID);
+    // update our last heard microstamp for this network peer to now
+    matchingPeer->setLastHeardMicrostamp(usecTimestampNow());
 
-    return false;
-}
-
-void IceServer::requestDomainPublicKey(const QUuid& domainID) {
-    // send a request to the metaverse API for the public key for this domain
-    QNetworkAccessManager* manager = new QNetworkAccessManager { this };
-    connect(manager, &QNetworkAccessManager::finished, this, &IceServer::publicKeyReplyFinished);
-
-    QUrl publicKeyURL { NetworkingConstants::METAVERSE_SERVER_URL };
-    QString publicKeyPath = QString("/api/v1/domains/%1/public_key").arg(uuidStringWithoutCurlyBraces(domainID));
-    publicKeyURL.setPath(publicKeyPath);
-
-    QNetworkRequest publicKeyRequest { publicKeyURL };
-    publicKeyRequest.setAttribute(QNetworkRequest::User, domainID);
-
-    qDebug() << "Requesting public key for domain with ID" << domainID;
-
-    manager->get(publicKeyRequest);
-}
-
-void IceServer::publicKeyReplyFinished(QNetworkReply* reply) {
-    // get the domain ID from the QNetworkReply attribute
-    QUuid domainID = reply->request().attribute(QNetworkRequest::User).toUuid();
-
-    if (reply->error() == QNetworkReply::NoError) {
-        // pull out the public key and store it for this domain
-
-        // the response should be JSON
-        QJsonDocument responseDocument = QJsonDocument::fromJson(reply->readAll());
-
-        static const QString DATA_KEY = "data";
-        static const QString PUBLIC_KEY_KEY = "public_key";
-        static const QString STATUS_KEY = "status";
-        static const QString SUCCESS_VALUE = "success";
-
-        auto responseObject = responseDocument.object();
-        if (responseObject[STATUS_KEY].toString() == SUCCESS_VALUE) {
-            auto dataObject = responseObject[DATA_KEY].toObject();
-            if (dataObject.contains(PUBLIC_KEY_KEY)) {
-                _domainPublicKeys[domainID] = QByteArray::fromBase64(dataObject[PUBLIC_KEY_KEY].toString().toUtf8());
-            } else {
-                qWarning() << "There was no public key present in response for domain with ID" << domainID;
-            }
-        } else {
-            qWarning() << "The metaverse API did not return success for public key request for domain with ID" << domainID;
-        }
-
-    } else {
-        // there was a problem getting the public key for the domain
-        // log it since it will be re-requested on the next heartbeat
-
-        qWarning() << "Error retreiving public key for domain with ID" << domainID << "-" <<  reply->errorString();
-    }
+    return matchingPeer;
 }
 
 void IceServer::sendPeerInformationPacket(const NetworkPeer& peer, const HifiSockAddr* destinationSockAddr) {
diff --git a/ice-server/src/IceServer.h b/ice-server/src/IceServer.h
index 81234b2c3c..f1c2c06b65 100644
--- a/ice-server/src/IceServer.h
+++ b/ice-server/src/IceServer.h
@@ -16,15 +16,13 @@
 #include <QtCore/QSharedPointer>
 #include <QUdpSocket>
 
-#include <UUIDHasher.h>
-
 #include <NetworkPeer.h>
 #include <HTTPConnection.h>
 #include <HTTPManager.h>
 #include <NLPacket.h>
 #include <udt/Socket.h>
 
-class QNetworkReply;
+typedef QHash<QUuid, SharedNetworkPeer> NetworkPeerHash;
 
 class IceServer : public QCoreApplication, public HTTPRequestHandler {
     Q_OBJECT
@@ -33,7 +31,6 @@ public:
     bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler = false);
 private slots:
     void clearInactivePeers();
-    void publicKeyReplyFinished(QNetworkReply* reply);
 private:
     bool packetVersionMatch(const udt::Packet& packet);
     void processPacket(std::unique_ptr<udt::Packet> packet);
@@ -41,19 +38,10 @@ private:
     SharedNetworkPeer addOrUpdateHeartbeatingPeer(NLPacket& incomingPacket);
     void sendPeerInformationPacket(const NetworkPeer& peer, const HifiSockAddr* destinationSockAddr);
 
-    bool isVerifiedHeartbeat(const QUuid& domainID, const QByteArray& plaintext, const QByteArray& signature);
-    void requestDomainPublicKey(const QUuid& domainID);
-
     QUuid _id;
     udt::Socket _serverSocket;
-
-    using NetworkPeerHash = QHash<QUuid, SharedNetworkPeer>;
     NetworkPeerHash _activePeers;
-
     HTTPManager _httpManager;
-
-    using DomainPublicKeyHash = std::unordered_map<QUuid, QByteArray>;
-    DomainPublicKeyHash _domainPublicKeys;
 };
 
 #endif // hifi_IceServer_h
diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index d4f9c32ac1..5bb666bf33 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -553,6 +553,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
     connect(&domainHandler, SIGNAL(connectedToDomain(const QString&)), SLOT(updateWindowTitle()));
     connect(&domainHandler, SIGNAL(disconnectedFromDomain()), SLOT(updateWindowTitle()));
     connect(&domainHandler, SIGNAL(disconnectedFromDomain()), SLOT(clearDomainOctreeDetails()));
+    connect(&domainHandler, &DomainHandler::settingsReceived, this, &Application::domainSettingsReceived);
 
     // update our location every 5 seconds in the metaverse server, assuming that we are authenticated with one
     const qint64 DATA_SERVER_LOCATION_CHANGE_UPDATE_MSECS = 5 * 1000;
@@ -589,7 +590,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
     connect(&accountManager, &AccountManager::usernameChanged, this, &Application::updateWindowTitle);
 
     // set the account manager's root URL and trigger a login request if we don't have the access token
-    accountManager.setIsAgent(true);
     accountManager.setAuthURL(NetworkingConstants::METAVERSE_SERVER_URL);
     UserActivityLogger::getInstance().launch(applicationVersion());
 
@@ -903,6 +903,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
     SpacemouseManager::getInstance().init();
 #endif
 
+    auto& packetReceiver = nodeList->getPacketReceiver();
+    packetReceiver.registerListener(PacketType::DomainConnectionDenied, this, "handleDomainConnectionDeniedPacket");
+
     // If the user clicks an an entity, we will check that it's an unlocked web entity, and if so, set the focus to it
     auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
     connect(entityScriptingInterface.data(), &EntityScriptingInterface::clickDownOnEntity,
@@ -3954,10 +3957,30 @@ void Application::clearDomainOctreeDetails() {
 void Application::domainChanged(const QString& domainHostname) {
     updateWindowTitle();
     clearDomainOctreeDetails();
+    _domainConnectionRefusals.clear();
     // disable physics until we have enough information about our new location to not cause craziness.
     _physicsEnabled = false;
 }
 
+void Application::handleDomainConnectionDeniedPacket(QSharedPointer<ReceivedMessage> message) {
+    // Read deny reason from packet
+    quint16 reasonSize;
+    message->readPrimitive(&reasonSize);
+    QString reason = QString::fromUtf8(message->readWithoutCopy(reasonSize));
+
+    // output to the log so the user knows they got a denied connection request
+    // and check and signal for an access token so that we can make sure they are logged in
+    qCDebug(interfaceapp) << "The domain-server denied a connection request: " << reason;
+    qCDebug(interfaceapp) << "You may need to re-log to generate a keypair so you can provide a username signature.";
+
+    if (!_domainConnectionRefusals.contains(reason)) {
+        _domainConnectionRefusals.append(reason);
+        emit domainConnectionRefused(reason);
+    }
+
+    AccountManager::getInstance().checkAndSignalForAccessToken();
+}
+
 void Application::connectedToDomain(const QString& hostname) {
     AccountManager& accountManager = AccountManager::getInstance();
     const QUuid& domainID = DependencyManager::get<NodeList>()->getDomainHandler().getUUID();
@@ -4501,6 +4524,33 @@ void Application::openUrl(const QUrl& url) {
     }
 }
 
+void Application::domainSettingsReceived(const QJsonObject& domainSettingsObject) {
+    // from the domain-handler, figure out the satoshi cost per voxel and per meter cubed
+    const QString VOXEL_SETTINGS_KEY = "voxels";
+    const QString PER_VOXEL_COST_KEY = "per-voxel-credits";
+    const QString PER_METER_CUBED_COST_KEY = "per-meter-cubed-credits";
+    const QString VOXEL_WALLET_UUID = "voxel-wallet";
+
+    const QJsonObject& voxelObject = domainSettingsObject[VOXEL_SETTINGS_KEY].toObject();
+
+    qint64 satoshisPerVoxel = 0;
+    qint64 satoshisPerMeterCubed = 0;
+    QUuid voxelWalletUUID;
+
+    if (!domainSettingsObject.isEmpty()) {
+        float perVoxelCredits = (float) voxelObject[PER_VOXEL_COST_KEY].toDouble();
+        float perMeterCubedCredits = (float) voxelObject[PER_METER_CUBED_COST_KEY].toDouble();
+
+        satoshisPerVoxel = (qint64) floorf(perVoxelCredits * SATOSHIS_PER_CREDIT);
+        satoshisPerMeterCubed = (qint64) floorf(perMeterCubedCredits * SATOSHIS_PER_CREDIT);
+
+        voxelWalletUUID = QUuid(voxelObject[VOXEL_WALLET_UUID].toString());
+    }
+
+    qCDebug(interfaceapp) << "Octree edits costs are" << satoshisPerVoxel << "per octree cell and" << satoshisPerMeterCubed << "per meter cubed";
+    qCDebug(interfaceapp) << "Destination wallet UUID for edit payments is" << voxelWalletUUID;
+}
+
 void Application::loadDialog() {
     auto scriptEngines = DependencyManager::get<ScriptEngines>();
     QString fileNameString = OffscreenUi::getOpenFileName(
diff --git a/interface/src/Application.h b/interface/src/Application.h
index cce968e331..51b90bcb99 100644
--- a/interface/src/Application.h
+++ b/interface/src/Application.h
@@ -224,6 +224,7 @@ signals:
     void svoImportRequested(const QString& url);
 
     void checkBackgroundDownloads();
+    void domainConnectionRefused(const QString& reason);
 
     void fullAvatarURLChanged(const QString& newValue, const QString& modelName);
 
@@ -291,6 +292,9 @@ private slots:
 
     void activeChanged(Qt::ApplicationState state);
 
+    void domainSettingsReceived(const QJsonObject& domainSettingsObject);
+    void handleDomainConnectionDeniedPacket(QSharedPointer<ReceivedMessage> message);
+
     void notifyPacketVersionMismatch();
 
     void loadSettings();
@@ -469,6 +473,7 @@ private:
     typedef bool (Application::* AcceptURLMethod)(const QString &);
     static const QHash<QString, AcceptURLMethod> _acceptedExtensions;
 
+    QList<QString> _domainConnectionRefusals;
     glm::uvec2 _renderResolution;
 
     int _maxOctreePPS = DEFAULT_MAX_OCTREE_PPS;
diff --git a/interface/src/CrashHandler.cpp b/interface/src/CrashHandler.cpp
index f9cd7679ae..ce5facb580 100644
--- a/interface/src/CrashHandler.cpp
+++ b/interface/src/CrashHandler.cpp
@@ -22,8 +22,11 @@
 #include <QStandardPaths>
 #include <QVBoxLayout>
 
+#include "DataServerAccountInfo.h"
 #include "Menu.h"
 
+Q_DECLARE_METATYPE(DataServerAccountInfo)
+
 static const QString RUNNING_MARKER_FILENAME = "Interface.running";
 
 void CrashHandler::checkForAndHandleCrash() {
@@ -54,7 +57,7 @@ CrashHandler::Action CrashHandler::promptUserForAction() {
     layout->addWidget(label);
 
     QRadioButton* option1 = new QRadioButton("Reset all my settings");
-    QRadioButton* option2 = new QRadioButton("Reset my settings but retain avatar info.");
+    QRadioButton* option2 = new QRadioButton("Reset my settings but retain login and avatar info.");
     QRadioButton* option3 = new QRadioButton("Continue with my current settings");
     option3->setChecked(true);
     layout->addWidget(option1);
@@ -76,7 +79,7 @@ CrashHandler::Action CrashHandler::promptUserForAction() {
             return CrashHandler::DELETE_INTERFACE_INI;
         }
         if (option2->isChecked()) {
-            return CrashHandler::RETAIN_AVATAR_INFO;
+            return CrashHandler::RETAIN_LOGIN_AND_AVATAR_INFO;
         }
     }
 
@@ -85,7 +88,7 @@ CrashHandler::Action CrashHandler::promptUserForAction() {
 }
 
 void CrashHandler::handleCrash(CrashHandler::Action action) {
-    if (action != CrashHandler::DELETE_INTERFACE_INI && action != CrashHandler::RETAIN_AVATAR_INFO) {
+    if (action != CrashHandler::DELETE_INTERFACE_INI && action != CrashHandler::RETAIN_LOGIN_AND_AVATAR_INFO) {
         // CrashHandler::DO_NOTHING or unexpected value
         return;
     }
@@ -98,13 +101,18 @@ void CrashHandler::handleCrash(CrashHandler::Action action) {
     const QString DISPLAY_NAME_KEY = "displayName";
     const QString FULL_AVATAR_URL_KEY = "fullAvatarURL";
     const QString FULL_AVATAR_MODEL_NAME_KEY = "fullAvatarModelName";
+    const QString ACCOUNTS_GROUP = "accounts";
     QString displayName;
     QUrl fullAvatarURL;
     QString fullAvatarModelName;
     QUrl address;
+    QMap<QString, DataServerAccountInfo> accounts;
 
-    if (action == CrashHandler::RETAIN_AVATAR_INFO) {
-        // Read avatar info
+    if (action == CrashHandler::RETAIN_LOGIN_AND_AVATAR_INFO) {
+        // Read login and avatar info
+
+        qRegisterMetaType<DataServerAccountInfo>("DataServerAccountInfo");
+        qRegisterMetaTypeStreamOperators<DataServerAccountInfo>("DataServerAccountInfo");
 
         // Location and orientation
         settings.beginGroup(ADDRESS_MANAGER_GROUP);
@@ -117,6 +125,13 @@ void CrashHandler::handleCrash(CrashHandler::Action action) {
         fullAvatarURL = settings.value(FULL_AVATAR_URL_KEY).toUrl();
         fullAvatarModelName = settings.value(FULL_AVATAR_MODEL_NAME_KEY).toString();
         settings.endGroup();
+
+        // Accounts
+        settings.beginGroup(ACCOUNTS_GROUP);
+        foreach(const QString& key, settings.allKeys()) {
+            accounts.insert(key, settings.value(key).value<DataServerAccountInfo>());
+        }
+        settings.endGroup();
     }
 
     // Delete Interface.ini
@@ -125,8 +140,8 @@ void CrashHandler::handleCrash(CrashHandler::Action action) {
         settingsFile.remove();
     }
 
-    if (action == CrashHandler::RETAIN_AVATAR_INFO) {
-        // Write avatar info
+    if (action == CrashHandler::RETAIN_LOGIN_AND_AVATAR_INFO) {
+        // Write login and avatar info
 
         // Location and orientation
         settings.beginGroup(ADDRESS_MANAGER_GROUP);
@@ -139,6 +154,13 @@ void CrashHandler::handleCrash(CrashHandler::Action action) {
         settings.setValue(FULL_AVATAR_URL_KEY, fullAvatarURL);
         settings.setValue(FULL_AVATAR_MODEL_NAME_KEY, fullAvatarModelName);
         settings.endGroup();
+
+        // Accounts
+        settings.beginGroup(ACCOUNTS_GROUP);
+        foreach(const QString& key, accounts.keys()) {
+            settings.setValue(key, QVariant::fromValue(accounts.value(key)));
+        }
+        settings.endGroup();
     }
 }
 
diff --git a/interface/src/CrashHandler.h b/interface/src/CrashHandler.h
index 61361b6107..fc754cf1ac 100644
--- a/interface/src/CrashHandler.h
+++ b/interface/src/CrashHandler.h
@@ -25,7 +25,7 @@ public:
 private:
     enum Action {
         DELETE_INTERFACE_INI,
-        RETAIN_AVATAR_INFO,
+        RETAIN_LOGIN_AND_AVATAR_INFO,
         DO_NOTHING
     };
 
diff --git a/interface/src/scripting/WindowScriptingInterface.cpp b/interface/src/scripting/WindowScriptingInterface.cpp
index 4c9c0df1cb..b8c82498a1 100644
--- a/interface/src/scripting/WindowScriptingInterface.cpp
+++ b/interface/src/scripting/WindowScriptingInterface.cpp
@@ -25,7 +25,7 @@
 WindowScriptingInterface::WindowScriptingInterface() {
     const DomainHandler& domainHandler = DependencyManager::get<NodeList>()->getDomainHandler();
     connect(&domainHandler, &DomainHandler::connectedToDomain, this, &WindowScriptingInterface::domainChanged);
-    connect(&domainHandler, &DomainHandler::domainConnectionRefused, this, &WindowScriptingInterface::domainConnectionRefused);
+    connect(qApp, &Application::domainConnectionRefused, this, &WindowScriptingInterface::domainConnectionRefused);
 
     connect(qApp, &Application::svoImportRequested, [this](const QString& urlString) {
         static const QMetaMethod svoImportRequestedSignal =
diff --git a/libraries/networking/src/AccountManager.cpp b/libraries/networking/src/AccountManager.cpp
index d0838c4a8f..4ded2216d0 100644
--- a/libraries/networking/src/AccountManager.cpp
+++ b/libraries/networking/src/AccountManager.cpp
@@ -12,12 +12,10 @@
 #include <memory>
 
 #include <QtCore/QDataStream>
-#include <QtCore/QFile>
 #include <QtCore/QJsonDocument>
 #include <QtCore/QJsonObject>
 #include <QtCore/QMap>
 #include <QtCore/QStringList>
-#include <QtCore/QStandardPaths>
 #include <QtCore/QUrlQuery>
 #include <QtNetwork/QHttpMultiPart>
 #include <QtNetwork/QNetworkRequest>
@@ -62,12 +60,13 @@ JSONCallbackParameters::JSONCallbackParameters(QObject* jsonCallbackReceiver, co
     updateReciever(updateReceiver),
     updateSlot(updateSlot)
 {
-    
 }
 
 AccountManager::AccountManager() :
     _authURL(),
-    _pendingCallbackMap()
+    _pendingCallbackMap(),
+    _accountInfo(),
+    _shouldPersistToSettingsFile(true)
 {
     qRegisterMetaType<OAuthAccessToken>("OAuthAccessToken");
     qRegisterMetaTypeStreamOperators<OAuthAccessToken>("OAuthAccessToken");
@@ -81,6 +80,9 @@ AccountManager::AccountManager() :
     qRegisterMetaType<QHttpMultiPart*>("QHttpMultiPart*");
 
     connect(&_accountInfo, &DataServerAccountInfo::balanceChanged, this, &AccountManager::accountInfoBalanceChanged);
+    
+    // once we have a profile in account manager make sure we generate a new keypair
+    connect(this, &AccountManager::profileChanged, this, &AccountManager::generateNewKeypair);
 }
 
 const QString DOUBLE_SLASH_SUBSTITUTE = "slashslash";
@@ -91,9 +93,16 @@ void AccountManager::logout() {
 
     emit balanceChanged(0);
     connect(&_accountInfo, &DataServerAccountInfo::balanceChanged, this, &AccountManager::accountInfoBalanceChanged);
-
-    // remove this account from the account settings file
-    removeAccountFromFile();
+    
+    if (_shouldPersistToSettingsFile) {
+        QString keyURLString(_authURL.toString().replace("//", DOUBLE_SLASH_SUBSTITUTE));
+        QStringList path = QStringList() << ACCOUNTS_GROUP << keyURLString;
+        Setting::Handle<DataServerAccountInfo>(path).remove();
+        
+        qCDebug(networking) << "Removed account info for" << _authURL << "from in-memory accounts and .ini file";
+    } else {
+        qCDebug(networking) << "Cleared data server account info in account manager.";
+    }
 
     emit logoutComplete();
     // the username has changed to blank
@@ -115,83 +124,35 @@ void AccountManager::accountInfoBalanceChanged(qint64 newBalance) {
     emit balanceChanged(newBalance);
 }
 
-QString accountFilePath() {
-    return QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/AccountInfo.bin";
-}
-
-QVariantMap accountMapFromFile(bool& success) {
-    QFile accountFile { accountFilePath() };
-
-    if (accountFile.open(QIODevice::ReadOnly)) {
-        // grab the current QVariantMap from the settings file
-        QDataStream readStream(&accountFile);
-        QVariantMap accountMap;
-
-        readStream >> accountMap;
-
-        // close the file now that we have read the data
-        accountFile.close();
-
-        success = true;
-
-        return accountMap;
-    } else {
-        // failed to open file, return empty QVariantMap
-        // there was only an error if the account file existed when we tried to load it
-        success = !accountFile.exists();
-
-        return QVariantMap();
-    }
-}
-
 void AccountManager::setAuthURL(const QUrl& authURL) {
     if (_authURL != authURL) {
         _authURL = authURL;
 
         qCDebug(networking) << "AccountManager URL for authenticated requests has been changed to" << qPrintable(_authURL.toString());
         
-        // check if there are existing access tokens to load from settings
-        QFile accountsFile { accountFilePath() };
-        bool loadedMap = false;
-        auto accountsMap = accountMapFromFile(loadedMap);
-
-        if (accountsFile.exists() && loadedMap) {
-            // pull out the stored account info and store it in memory
-            _accountInfo = accountsMap[_authURL.toString()].value<DataServerAccountInfo>();
-
-            qCDebug(networking) << "Found metaverse API account information for" << qPrintable(_authURL.toString());
-        } else {
-            // we didn't have a file - see if we can migrate old settings and store them in the new file
-
+        if (_shouldPersistToSettingsFile) {
             // check if there are existing access tokens to load from settings
             Settings settings;
             settings.beginGroup(ACCOUNTS_GROUP);
-
+            
             foreach(const QString& key, settings.allKeys()) {
                 // take a key copy to perform the double slash replacement
                 QString keyCopy(key);
                 QUrl keyURL(keyCopy.replace(DOUBLE_SLASH_SUBSTITUTE, "//"));
-
+                
                 if (keyURL == _authURL) {
                     // pull out the stored access token and store it in memory
                     _accountInfo = settings.value(key).value<DataServerAccountInfo>();
-
-                    qCDebug(networking) << "Migrated an access token for" << qPrintable(keyURL.toString())
-                        <<  "from previous settings file";
+                    qCDebug(networking) << "Found a data-server access token for" << qPrintable(keyURL.toString());
+                    
+                    // profile info isn't guaranteed to be saved too
+                    if (_accountInfo.hasProfile()) {
+                        emit profileChanged();
+                    } else {
+                        requestProfile();
+                    }
                 }
             }
-
-            if (_accountInfo.getAccessToken().token.isEmpty()) {
-                qCWarning(networking) << "Unable to load account file. No existing account settings will be loaded.";
-            } else {
-                // persist the migrated settings to file
-                persistAccountToFile();
-            }
-        }
-
-        if (_isAgent && !_accountInfo.getAccessToken().token.isEmpty() && !_accountInfo.hasProfile()) {
-            // we are missing profile information, request it now
-            requestProfile();
         }
 
         // tell listeners that the auth endpoint has changed
@@ -338,11 +299,9 @@ void AccountManager::passSuccessToCallback(QNetworkReply* requestReply) {
 
     } else {
         if (VERBOSE_HTTP_REQUEST_DEBUGGING) {
-            qCDebug(networking) << "Received JSON response from metaverse API that has no matching callback.";
+            qCDebug(networking) << "Received JSON response from data-server that has no matching callback.";
             qCDebug(networking) << QJsonDocument::fromJson(requestReply->readAll());
         }
-
-        requestReply->deleteLater();
     }
 }
 
@@ -358,69 +317,22 @@ void AccountManager::passErrorToCallback(QNetworkReply* requestReply) {
         _pendingCallbackMap.remove(requestReply);
     } else {
         if (VERBOSE_HTTP_REQUEST_DEBUGGING) {
-            qCDebug(networking) << "Received error response from metaverse API that has no matching callback.";
+            qCDebug(networking) << "Received error response from data-server that has no matching callback.";
             qCDebug(networking) << "Error" << requestReply->error() << "-" << requestReply->errorString();
             qCDebug(networking) << requestReply->readAll();
         }
-
-        requestReply->deleteLater();
     }
 }
 
-bool writeAccountMapToFile(const QVariantMap& accountMap) {
-    // re-open the file and truncate it
-    QFile accountFile { accountFilePath() };
-    if (accountFile.open(QIODevice::WriteOnly)) {
-        QDataStream writeStream(&accountFile);
-
-        // persist the updated account QVariantMap to file
-        writeStream << accountMap;
-
-        // close the file with the newly persisted settings
-        accountFile.close();
-
-        return true;
-    } else {
-        return false;
+void AccountManager::persistAccountToSettings() {
+    if (_shouldPersistToSettingsFile) {
+        // store this access token into the local settings
+        QString keyURLString(_authURL.toString().replace("//", DOUBLE_SLASH_SUBSTITUTE));
+        QStringList path = QStringList() << ACCOUNTS_GROUP << keyURLString;
+        Setting::Handle<QVariant>(path).set(QVariant::fromValue(_accountInfo));
     }
 }
 
-void AccountManager::persistAccountToFile() {
-
-    qCDebug(networking) << "Persisting AccountManager accounts to" << accountFilePath();
-
-    bool wasLoaded = false;
-    auto accountMap = accountMapFromFile(wasLoaded);
-
-    if (wasLoaded) {
-        // replace the current account information for this auth URL in the account map
-        accountMap[_authURL.toString()] = QVariant::fromValue(_accountInfo);
-
-        // re-open the file and truncate it
-        if (writeAccountMapToFile(accountMap)) {
-            return;
-        }
-    }
-
-    qCWarning(networking) << "Could not load accounts file - unable to persist account information to file.";
-}
-
-void AccountManager::removeAccountFromFile() {
-    bool wasLoaded = false;
-    auto accountMap = accountMapFromFile(wasLoaded);
-
-    if (wasLoaded) {
-        accountMap.remove(_authURL.toString());
-        if (writeAccountMapToFile(accountMap)) {
-            qCDebug(networking) << "Removed account info for" << _authURL << "from settings file.";
-            return;
-        }
-    }
-
-    qCWarning(networking) << "Count not load accounts file - unable to remove account information for" << _authURL
-        << "from settings file.";
-}
-
 bool AccountManager::hasValidAccessToken() {
     
     if (_accountInfo.getAccessToken().token.isEmpty() || _accountInfo.getAccessToken().isExpired()) {
@@ -447,19 +359,16 @@ bool AccountManager::checkAndSignalForAccessToken() {
 }
 
 void AccountManager::setAccessTokenForCurrentAuthURL(const QString& accessToken) {
-    // replace the account info access token with a new OAuthAccessToken
+    // clear our current DataServerAccountInfo
+    _accountInfo = DataServerAccountInfo();
+    
+    // start the new account info with a new OAuthAccessToken
     OAuthAccessToken newOAuthToken;
     newOAuthToken.token = accessToken;
-
-    if (!accessToken.isEmpty()) {
-        qCDebug(networking) << "Setting new AccountManager OAuth token. F2C:" << accessToken.left(2) << "L2C:" << accessToken.right(2);
-    } else if (!_accountInfo.getAccessToken().token.isEmpty()) {
-        qCDebug(networking) << "Clearing AccountManager OAuth token.";
-    }
+    
+    qCDebug(networking) << "Setting new account manager access token. F2C:" << accessToken.left(2) << "L2C:" << accessToken.right(2);
     
     _accountInfo.setAccessToken(newOAuthToken);
-
-    persistAccountToFile();
 }
 
 void AccountManager::requestAccessToken(const QString& login, const QString& password) {
@@ -514,7 +423,7 @@ void AccountManager::requestAccessTokenFinished() {
 
             emit loginComplete(rootURL);
             
-            persistAccountToFile();
+            persistAccountToSettings();
 
             requestProfile();
         }
@@ -560,7 +469,7 @@ void AccountManager::requestProfileFinished() {
         emit usernameChanged(_accountInfo.getUsername());
 
         // store the whole profile into the local settings
-        persistAccountToFile();
+        persistAccountToSettings();
         
     } else {
         // TODO: error handling
@@ -573,141 +482,57 @@ void AccountManager::requestProfileError(QNetworkReply::NetworkError error) {
     qCDebug(networking) << "AccountManager requestProfileError - " << error;
 }
 
-void AccountManager::generateNewKeypair(bool isUserKeypair, const QUuid& domainID) {
-
-    if (thread() != QThread::currentThread()) {
-        QMetaObject::invokeMethod(this, "generateNewKeypair", Q_ARG(bool, isUserKeypair), Q_ARG(QUuid, domainID));
-        return;
-    }
-
-    if (!isUserKeypair && domainID.isNull()) {
-        qCWarning(networking) << "AccountManager::generateNewKeypair called for domain keypair with no domain ID. Will not generate keypair.";
-        return;
-    }
-
-    // make sure we don't already have an outbound keypair generation request
-    if (!_isWaitingForKeypairResponse) {
-        _isWaitingForKeypairResponse = true;
-
-        // clear the current private key
-        qDebug() << "Clearing current private key in DataServerAccountInfo";
-        _accountInfo.setPrivateKey(QByteArray());
-
-        // setup a new QThread to generate the keypair on, in case it takes a while
-        QThread* generateThread = new QThread(this);
-        generateThread->setObjectName("Account Manager Generator Thread");
-
-        // setup a keypair generator
-        RSAKeypairGenerator* keypairGenerator = new RSAKeypairGenerator;
-
-        if (!isUserKeypair) {
-            keypairGenerator->setDomainID(domainID);
-            _accountInfo.setDomainID(domainID);
-        }
-
-        // start keypair generation when the thread starts
-        connect(generateThread, &QThread::started, keypairGenerator, &RSAKeypairGenerator::generateKeypair);
-
-        // handle success or failure of keypair generation
-        connect(keypairGenerator, &RSAKeypairGenerator::generatedKeypair, this, &AccountManager::processGeneratedKeypair);
-        connect(keypairGenerator, &RSAKeypairGenerator::errorGeneratingKeypair,
-                this, &AccountManager::handleKeypairGenerationError);
-
-        connect(keypairGenerator, &QObject::destroyed, generateThread, &QThread::quit);
-        connect(generateThread, &QThread::finished, generateThread, &QThread::deleteLater);
-
-        keypairGenerator->moveToThread(generateThread);
-        
-        qCDebug(networking) << "Starting worker thread to generate 2048-bit RSA keypair.";
-        generateThread->start();
-    }
-}
-
-void AccountManager::processGeneratedKeypair() {
+void AccountManager::generateNewKeypair() {
+    // setup a new QThread to generate the keypair on, in case it takes a while
+    QThread* generateThread = new QThread(this);
+    generateThread->setObjectName("Account Manager Generator Thread");
     
-    qCDebug(networking) << "Generated 2048-bit RSA keypair. Uploading public key now.";
-
-    RSAKeypairGenerator* keypairGenerator = qobject_cast<RSAKeypairGenerator*>(sender());
-
-    if (keypairGenerator) {
-        // hold the private key to later set our metaverse API account info if upload succeeds
-        _pendingPrivateKey = keypairGenerator->getPrivateKey();
-
-        // upload the public key so data-web has an up-to-date key
-        const QString USER_PUBLIC_KEY_UPDATE_PATH = "api/v1/user/public_key";
-        const QString DOMAIN_PUBLIC_KEY_UPDATE_PATH = "api/v1/domains/%1/public_key";
-
-        QString uploadPath;
-        if (keypairGenerator->getDomainID().isNull()) {
-            uploadPath = USER_PUBLIC_KEY_UPDATE_PATH;
-        } else {
-            uploadPath = DOMAIN_PUBLIC_KEY_UPDATE_PATH.arg(uuidStringWithoutCurlyBraces(keypairGenerator->getDomainID()));
-        }
-
-        // setup a multipart upload to send up the public key
-        QHttpMultiPart* requestMultiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
-
-        QHttpPart keyPart;
-        keyPart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/octet-stream"));
-        keyPart.setHeader(QNetworkRequest::ContentDispositionHeader,
-                          QVariant("form-data; name=\"public_key\"; filename=\"public_key\""));
-        keyPart.setBody(keypairGenerator->getPublicKey());
-
-        requestMultiPart->append(keyPart);
-
-        // setup callback parameters so we know once the keypair upload has succeeded or failed
-        JSONCallbackParameters callbackParameters;
-        callbackParameters.jsonCallbackReceiver = this;
-        callbackParameters.jsonCallbackMethod = "publicKeyUploadSucceeded";
-        callbackParameters.errorCallbackReceiver = this;
-        callbackParameters.errorCallbackMethod = "publicKeyUploadFailed";
-
-        sendRequest(uploadPath, AccountManagerAuth::Optional, QNetworkAccessManager::PutOperation,
-                    callbackParameters, QByteArray(), requestMultiPart);
-        
-        keypairGenerator->deleteLater();
-    } else {
-        qCWarning(networking) << "Expected processGeneratedKeypair to be called by a live RSAKeypairGenerator"
-            << "but the casted sender is NULL. Will not process generated keypair.";
-    }
+    // setup a keypair generator
+    RSAKeypairGenerator* keypairGenerator = new RSAKeypairGenerator();
+    
+    connect(generateThread, &QThread::started, keypairGenerator, &RSAKeypairGenerator::generateKeypair);
+    connect(keypairGenerator, &RSAKeypairGenerator::generatedKeypair, this, &AccountManager::processGeneratedKeypair);
+    connect(keypairGenerator, &RSAKeypairGenerator::errorGeneratingKeypair,
+            this, &AccountManager::handleKeypairGenerationError);
+    connect(keypairGenerator, &QObject::destroyed, generateThread, &QThread::quit);
+    connect(generateThread, &QThread::finished, generateThread, &QThread::deleteLater);
+    
+    keypairGenerator->moveToThread(generateThread);
+    
+    qCDebug(networking) << "Starting worker thread to generate 2048-bit RSA key-pair.";
+    generateThread->start();
 }
 
-void AccountManager::publicKeyUploadSucceeded(QNetworkReply& reply) {
-    qDebug() << "Uploaded public key to Metaverse API. RSA keypair generation is completed.";
-
-    // public key upload complete - store the matching private key and persist the account to settings
-    _accountInfo.setPrivateKey(_pendingPrivateKey);
-    _pendingPrivateKey.clear();
-    persistAccountToFile();
-
-    // clear our waiting state
-    _isWaitingForKeypairResponse = false;
-
-    emit newKeypair();
-
-    // delete the reply object now that we are done with it
-    reply.deleteLater();
-}
-
-void AccountManager::publicKeyUploadFailed(QNetworkReply& reply) {
-    // the public key upload has failed
-    qWarning() << "Public key upload failed from AccountManager" << reply.errorString();
-
-    // we aren't waiting for a response any longer
-    _isWaitingForKeypairResponse = false;
-
-    // clear our pending private key
-    _pendingPrivateKey.clear();
-
-    // delete the reply object now that we are done with it
-    reply.deleteLater();
+void AccountManager::processGeneratedKeypair(const QByteArray& publicKey, const QByteArray& privateKey) {
+    
+    qCDebug(networking) << "Generated 2048-bit RSA key-pair. Storing private key and uploading public key.";
+    
+    // set the private key on our data-server account info
+    _accountInfo.setPrivateKey(privateKey);
+    persistAccountToSettings();
+    
+    // upload the public key so data-web has an up-to-date key
+    const QString PUBLIC_KEY_UPDATE_PATH = "api/v1/user/public_key";
+    
+    // setup a multipart upload to send up the public key
+    QHttpMultiPart* requestMultiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
+    
+    QHttpPart keyPart;
+    keyPart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/octet-stream"));
+    keyPart.setHeader(QNetworkRequest::ContentDispositionHeader,
+                      QVariant("form-data; name=\"public_key\"; filename=\"public_key\""));
+    keyPart.setBody(publicKey);
+    
+    requestMultiPart->append(keyPart);
+    
+    sendRequest(PUBLIC_KEY_UPDATE_PATH, AccountManagerAuth::Required, QNetworkAccessManager::PutOperation,
+                JSONCallbackParameters(), QByteArray(), requestMultiPart);
+    
+    // get rid of the keypair generator now that we don't need it anymore
+    sender()->deleteLater();
 }
 
 void AccountManager::handleKeypairGenerationError() {
-    qCritical() << "Error generating keypair - this is likely to cause authentication issues.";
-
-    // reset our waiting state for keypair response
-    _isWaitingForKeypairResponse = false;
-
+    // for now there isn't anything we do with this except get the worker thread to clean up
     sender()->deleteLater();
 }
diff --git a/libraries/networking/src/AccountManager.h b/libraries/networking/src/AccountManager.h
index 0ebefafbed..719279b0cf 100644
--- a/libraries/networking/src/AccountManager.h
+++ b/libraries/networking/src/AccountManager.h
@@ -62,12 +62,12 @@ public:
                                  QHttpMultiPart* dataMultiPart = NULL,
                                  const QVariantMap& propertyMap = QVariantMap());
 
-    void setIsAgent(bool isAgent) { _isAgent = isAgent; }
-
     const QUrl& getAuthURL() const { return _authURL; }
     void setAuthURL(const QUrl& authURL);
     bool hasAuthEndpoint() { return !_authURL.isEmpty(); }
 
+    void disableSettingsFilePersistence() { _shouldPersistToSettingsFile = false; }
+
     bool isLoggedIn() { return !_authURL.isEmpty() && hasValidAccessToken(); }
     bool hasValidAccessToken();
     Q_INVOKABLE bool checkAndSignalForAccessToken();
@@ -87,9 +87,7 @@ public slots:
     void logout();
     void updateBalance();
     void accountInfoBalanceChanged(qint64 newBalance);
-    void generateNewUserKeypair() { generateNewKeypair(); }
-    void generateNewDomainKeypair(const QUuid& domainID) { generateNewKeypair(false, domainID); }
-
+    void generateNewKeypair();
 signals:
     void authRequired();
     void authEndpointChanged();
@@ -99,36 +97,25 @@ signals:
     void loginFailed();
     void logoutComplete();
     void balanceChanged(qint64 newBalance);
-    void newKeypair();
-
 private slots:
     void processReply();
     void handleKeypairGenerationError();
-    void processGeneratedKeypair();
-    void publicKeyUploadSucceeded(QNetworkReply& reply);
-    void publicKeyUploadFailed(QNetworkReply& reply);
-    void generateNewKeypair(bool isUserKeypair = true, const QUuid& domainID = QUuid());
-
+    void processGeneratedKeypair(const QByteArray& publicKey, const QByteArray& privateKey);
 private:
     AccountManager();
-    AccountManager(AccountManager const& other) = delete;
-    void operator=(AccountManager const& other) = delete;
+    AccountManager(AccountManager const& other); // not implemented
+    void operator=(AccountManager const& other); // not implemented
 
-    void persistAccountToFile();
-    void removeAccountFromFile();
+    void persistAccountToSettings();
 
     void passSuccessToCallback(QNetworkReply* reply);
     void passErrorToCallback(QNetworkReply* reply);
 
     QUrl _authURL;
-    
     QMap<QNetworkReply*, JSONCallbackParameters> _pendingCallbackMap;
 
     DataServerAccountInfo _accountInfo;
-    bool _isAgent { false };
-
-    bool _isWaitingForKeypairResponse { false };
-    QByteArray _pendingPrivateKey;
+    bool _shouldPersistToSettingsFile;
 };
 
 #endif // hifi_AccountManager_h
diff --git a/libraries/networking/src/DataServerAccountInfo.cpp b/libraries/networking/src/DataServerAccountInfo.cpp
index 9455fb1b88..5d633a8df1 100644
--- a/libraries/networking/src/DataServerAccountInfo.cpp
+++ b/libraries/networking/src/DataServerAccountInfo.cpp
@@ -25,6 +25,19 @@
 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
 #endif
 
+DataServerAccountInfo::DataServerAccountInfo() :
+    _accessToken(),
+    _username(),
+    _xmppPassword(),
+    _discourseApiKey(),
+    _walletID(),
+    _balance(0),
+    _hasBalance(false),
+    _privateKey()
+{
+
+}
+
 DataServerAccountInfo::DataServerAccountInfo(const DataServerAccountInfo& otherInfo) : QObject() {
     _accessToken = otherInfo._accessToken;
     _username = otherInfo._username;
@@ -34,7 +47,6 @@ DataServerAccountInfo::DataServerAccountInfo(const DataServerAccountInfo& otherI
     _balance = otherInfo._balance;
     _hasBalance = otherInfo._hasBalance;
     _privateKey = otherInfo._privateKey;
-    _domainID = otherInfo._domainID;
 }
 
 DataServerAccountInfo& DataServerAccountInfo::operator=(const DataServerAccountInfo& otherInfo) {
@@ -54,7 +66,6 @@ void DataServerAccountInfo::swap(DataServerAccountInfo& otherInfo) {
     swap(_balance, otherInfo._balance);
     swap(_hasBalance, otherInfo._hasBalance);
     swap(_privateKey, otherInfo._privateKey);
-    swap(_domainID, otherInfo._domainID);
 }
 
 void DataServerAccountInfo::setAccessTokenFromJSON(const QJsonObject& jsonObject) {
@@ -117,62 +128,59 @@ void DataServerAccountInfo::setProfileInfoFromJSON(const QJsonObject& jsonObject
 }
 
 QByteArray DataServerAccountInfo::getUsernameSignature(const QUuid& connectionToken) {
-    auto lowercaseUsername = _username.toLower().toUtf8();
-    auto plaintext = lowercaseUsername.append(connectionToken.toRfc4122());
+    
+        if (!_privateKey.isEmpty()) {
+            const char* privateKeyData = _privateKey.constData();
+            RSA* rsaPrivateKey = d2i_RSAPrivateKey(NULL,
+                                                   reinterpret_cast<const unsigned char**>(&privateKeyData),
+                                                   _privateKey.size());
+            if (rsaPrivateKey) {
+                QByteArray lowercaseUsername = _username.toLower().toUtf8();
+                QByteArray usernameWithToken = QCryptographicHash::hash(lowercaseUsername.append(connectionToken.toRfc4122()),
+                                                                        QCryptographicHash::Sha256);
+                
+                QByteArray usernameSignature(RSA_size(rsaPrivateKey), 0);
+                unsigned int usernameSignatureSize = 0;
+                
+                int encryptReturn = RSA_sign(NID_sha256,
+                                             reinterpret_cast<const unsigned char*>(usernameWithToken.constData()),
+                                             usernameWithToken.size(),
+                                             reinterpret_cast<unsigned char*>(usernameSignature.data()),
+                                             &usernameSignatureSize,
+                                             rsaPrivateKey);
+                
+                // free the private key RSA struct now that we are done with it
+                RSA_free(rsaPrivateKey);
 
-    auto signature = signPlaintext(plaintext);
-    if (!signature.isEmpty()) {
-        qDebug(networking) << "Returning username" << _username
-            << "signed with connection UUID" << uuidStringWithoutCurlyBraces(connectionToken);
-    } else {
-        qCDebug(networking) << "Error signing username with connection token";
-        qCDebug(networking) << "Will re-attempt on next domain-server check in.";
-    }
-
-    return signature;
+                if (encryptReturn == -1) {
+                    qCDebug(networking) << "Error encrypting username signature.";
+                    qCDebug(networking) << "Will re-attempt on next domain-server check in.";
+                } else {
+                    qDebug(networking) << "Returning username" << _username << "signed with connection UUID" << uuidStringWithoutCurlyBraces(connectionToken);
+                    return usernameSignature;
+                }
+                
+            } else {
+                qCDebug(networking) << "Could not create RSA struct from QByteArray private key.";
+                qCDebug(networking) << "Will re-attempt on next domain-server check in.";
+            }
+        }
+    return QByteArray();
 }
 
-QByteArray DataServerAccountInfo::signPlaintext(const QByteArray& plaintext) {
-    if (!_privateKey.isEmpty()) {
-        const char* privateKeyData = _privateKey.constData();
-        RSA* rsaPrivateKey = d2i_RSAPrivateKey(NULL,
-                                               reinterpret_cast<const unsigned char**>(&privateKeyData),
-                                               _privateKey.size());
-        if (rsaPrivateKey) {
-            QByteArray signature(RSA_size(rsaPrivateKey), 0);
-            unsigned int signatureBytes = 0;
-
-            QByteArray hashedPlaintext = QCryptographicHash::hash(plaintext, QCryptographicHash::Sha256);
-
-            int encryptReturn = RSA_sign(NID_sha256,
-                                         reinterpret_cast<const unsigned char*>(hashedPlaintext.constData()),
-                                         hashedPlaintext.size(),
-                                         reinterpret_cast<unsigned char*>(signature.data()),
-                                         &signatureBytes,
-                                         rsaPrivateKey);
-
-            // free the private key RSA struct now that we are done with it
-            RSA_free(rsaPrivateKey);
-
-            if (encryptReturn != -1) {
-                return signature;
-            }
-        } else {
-            qCDebug(networking) << "Could not create RSA struct from QByteArray private key.";
-        }
-    }
-    return QByteArray();
+void DataServerAccountInfo::setPrivateKey(const QByteArray& privateKey) {
+    _privateKey = privateKey;
+    
 }
 
 QDataStream& operator<<(QDataStream &out, const DataServerAccountInfo& info) {
     out << info._accessToken << info._username << info._xmppPassword << info._discourseApiKey
-        << info._walletID << info._privateKey << info._domainID;
-
+        << info._walletID << info._privateKey;
     return out;
 }
 
 QDataStream& operator>>(QDataStream &in, DataServerAccountInfo& info) {
     in >> info._accessToken >> info._username >> info._xmppPassword >> info._discourseApiKey
-        >> info._walletID >> info._privateKey >> info._domainID;
+        >> info._walletID >> info._privateKey;
     return in;
 }
diff --git a/libraries/networking/src/DataServerAccountInfo.h b/libraries/networking/src/DataServerAccountInfo.h
index 6223bc008e..9b80de5422 100644
--- a/libraries/networking/src/DataServerAccountInfo.h
+++ b/libraries/networking/src/DataServerAccountInfo.h
@@ -23,7 +23,7 @@ const float SATOSHIS_PER_CREDIT = 100000000.0f;
 class DataServerAccountInfo : public QObject {
     Q_OBJECT
 public:
-    DataServerAccountInfo() {};
+    DataServerAccountInfo();
     DataServerAccountInfo(const DataServerAccountInfo& otherInfo);
     DataServerAccountInfo& operator=(const DataServerAccountInfo& otherInfo);
 
@@ -42,6 +42,10 @@ public:
     
     const QUuid& getWalletID() const { return _walletID; }
     void setWalletID(const QUuid& walletID);
+    
+    QByteArray getUsernameSignature(const QUuid& connectionToken);
+    bool hasPrivateKey() const { return !_privateKey.isEmpty(); }
+    void setPrivateKey(const QByteArray& privateKey);
 
     qint64 getBalance() const { return _balance; }
     float getBalanceInSatoshis() const { return _balance / SATOSHIS_PER_CREDIT; }
@@ -50,15 +54,6 @@ public:
     void setHasBalance(bool hasBalance) { _hasBalance = hasBalance; }
     Q_INVOKABLE void setBalanceFromJSON(QNetworkReply& requestReply);
 
-    QByteArray getUsernameSignature(const QUuid& connectionToken);
-    bool hasPrivateKey() const { return !_privateKey.isEmpty(); }
-    void setPrivateKey(const QByteArray& privateKey) { _privateKey = privateKey; }
-
-    QByteArray signPlaintext(const QByteArray& plaintext);
-
-    void setDomainID(const QUuid& domainID) { _domainID = domainID; }
-    const QUuid& getDomainID() const { return _domainID; }
-
     bool hasProfile() const;
 
     void setProfileInfoFromJSON(const QJsonObject& jsonObject);
@@ -75,9 +70,8 @@ private:
     QString _xmppPassword;
     QString _discourseApiKey;
     QUuid _walletID;
-    qint64 _balance { 0 };
-    bool _hasBalance { false };
-    QUuid _domainID; // if this holds account info for a domain, this holds the ID of that domain
+    qint64 _balance;
+    bool _hasBalance;
     QByteArray _privateKey;
 
 };
diff --git a/libraries/networking/src/DomainHandler.cpp b/libraries/networking/src/DomainHandler.cpp
index 34ca722537..db775983e1 100644
--- a/libraries/networking/src/DomainHandler.cpp
+++ b/libraries/networking/src/DomainHandler.cpp
@@ -92,9 +92,7 @@ void DomainHandler::softReset() {
     disconnect();
     
     clearSettings();
-
-    _connectionDenialsSinceKeypairRegen = 0;
-
+    
     // cancel the failure timeout for any pending requests for settings
     QMetaObject::invokeMethod(&_settingsTimer, "stop");
 }
@@ -108,9 +106,6 @@ void DomainHandler::hardReset() {
     _hostname = QString();
     _sockAddr.clear();
 
-    _hasCheckedForAccessToken = false;
-    _domainConnectionRefusals.clear();
-
     // clear any pending path we may have wanted to ask the previous DS about
     _pendingPath.clear();
 }
@@ -352,35 +347,3 @@ void DomainHandler::processICEResponsePacket(QSharedPointer<ReceivedMessage> mes
         emit icePeerSocketsReceived();
     }
 }
-
-void DomainHandler::processDomainServerConnectionDeniedPacket(QSharedPointer<ReceivedMessage> message) {
-    // Read deny reason from packet
-    quint16 reasonSize;
-    message->readPrimitive(&reasonSize);
-    QString reason = QString::fromUtf8(message->readWithoutCopy(reasonSize));
-
-    // output to the log so the user knows they got a denied connection request
-    // and check and signal for an access token so that we can make sure they are logged in
-    qCWarning(networking) << "The domain-server denied a connection request: " << reason;
-    qCWarning(networking) << "Make sure you are logged in.";
-
-    if (!_domainConnectionRefusals.contains(reason)) {
-        _domainConnectionRefusals.append(reason);
-        emit domainConnectionRefused(reason);
-    }
-
-    auto& accountManager = AccountManager::getInstance();
-
-    if (!_hasCheckedForAccessToken) {
-        accountManager.checkAndSignalForAccessToken();
-        _hasCheckedForAccessToken = true;
-    }
-
-    static const int CONNECTION_DENIALS_FOR_KEYPAIR_REGEN = 3;
-
-    // force a re-generation of key-pair after CONNECTION_DENIALS_FOR_KEYPAIR_REGEN failed connection attempts
-    if (++_connectionDenialsSinceKeypairRegen >= CONNECTION_DENIALS_FOR_KEYPAIR_REGEN) {
-        accountManager.generateNewUserKeypair();
-        _connectionDenialsSinceKeypairRegen = 0;
-    }
-}
diff --git a/libraries/networking/src/DomainHandler.h b/libraries/networking/src/DomainHandler.h
index b245305a93..f60ac2fbe6 100644
--- a/libraries/networking/src/DomainHandler.h
+++ b/libraries/networking/src/DomainHandler.h
@@ -92,7 +92,6 @@ public slots:
     void processICEPingReplyPacket(QSharedPointer<ReceivedMessage> message);
     void processDTLSRequirementPacket(QSharedPointer<ReceivedMessage> dtlsRequirementPacket);
     void processICEResponsePacket(QSharedPointer<ReceivedMessage> icePacket);
-    void processDomainServerConnectionDeniedPacket(QSharedPointer<ReceivedMessage> message);
 
 private slots:
     void completedHostnameLookup(const QHostInfo& hostInfo);
@@ -114,8 +113,6 @@ signals:
     void settingsReceived(const QJsonObject& domainSettingsObject);
     void settingsReceiveFail();
 
-    void domainConnectionRefused(QString reason);
-
 private:
     void sendDisconnectPacket();
     void hardReset();
@@ -133,10 +130,6 @@ private:
     QJsonObject _settingsObject;
     QString _pendingPath;
     QTimer _settingsTimer;
-
-    QStringList _domainConnectionRefusals;
-    bool _hasCheckedForAccessToken { false };
-    int _connectionDenialsSinceKeypairRegen { 0 };
 };
 
 #endif // hifi_DomainHandler_h
diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp
index f236f9d596..a3707d19ba 100644
--- a/libraries/networking/src/LimitedNodeList.cpp
+++ b/libraries/networking/src/LimitedNodeList.cpp
@@ -902,6 +902,10 @@ void LimitedNodeList::updateLocalSockAddr() {
     }
 }
 
+void LimitedNodeList::sendHeartbeatToIceServer(const HifiSockAddr& iceServerSockAddr) {
+    sendPacketToIceServer(PacketType::ICEServerHeartbeat, iceServerSockAddr, _sessionUUID);
+}
+
 void LimitedNodeList::sendPeerQueryToIceServer(const HifiSockAddr& iceServerSockAddr, const QUuid& clientID,
                                                const QUuid& peerID) {
     sendPacketToIceServer(PacketType::ICEServerQuery, iceServerSockAddr, clientID, peerID);
diff --git a/libraries/networking/src/LimitedNodeList.h b/libraries/networking/src/LimitedNodeList.h
index de110c7e7f..fcad23da8f 100644
--- a/libraries/networking/src/LimitedNodeList.h
+++ b/libraries/networking/src/LimitedNodeList.h
@@ -143,7 +143,6 @@ public:
     bool hasCompletedInitialSTUN() const { return _hasCompletedInitialSTUN; }
 
     const HifiSockAddr& getLocalSockAddr() const { return _localSockAddr; }
-    const HifiSockAddr& getPublicSockAddr() const { return _publicSockAddr; }
     const HifiSockAddr& getSTUNSockAddr() const { return _stunSockAddr; }
 
     void processKillNode(ReceivedMessage& message);
@@ -162,6 +161,7 @@ public:
     std::unique_ptr<NLPacket> constructICEPingPacket(PingType_t pingType, const QUuid& iceID);
     std::unique_ptr<NLPacket> constructICEPingReplyPacket(ReceivedMessage& message, const QUuid& iceID);
 
+    void sendHeartbeatToIceServer(const HifiSockAddr& iceServerSockAddr);
     void sendPeerQueryToIceServer(const HifiSockAddr& iceServerSockAddr, const QUuid& clientID, const QUuid& peerID);
 
     SharedNodePointer findNodeWithAddr(const HifiSockAddr& addr);
diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp
index 02bb17e870..677a1ad1e6 100644
--- a/libraries/networking/src/NodeList.cpp
+++ b/libraries/networking/src/NodeList.cpp
@@ -80,16 +80,11 @@ NodeList::NodeList(char newOwnerType, unsigned short socketListenPort, unsigned
     // send a ping punch immediately
     connect(&_domainHandler, &DomainHandler::icePeerSocketsReceived, this, &NodeList::pingPunchForDomainServer);
 
-    auto &accountManager = AccountManager::getInstance();
-    
-    // assume that we may need to send a new DS check in anytime a new keypair is generated 
-    connect(&accountManager, &AccountManager::newKeypair, this, &NodeList::sendDomainServerCheckIn);
-
     // clear out NodeList when login is finished
-    connect(&accountManager, &AccountManager::loginComplete , this, &NodeList::reset);
+    connect(&AccountManager::getInstance(), &AccountManager::loginComplete , this, &NodeList::reset);
 
     // clear our NodeList when logout is requested
-    connect(&accountManager, &AccountManager::logoutComplete , this, &NodeList::reset);
+    connect(&AccountManager::getInstance(), &AccountManager::logoutComplete , this, &NodeList::reset);
 
     // anytime we get a new node we will want to attempt to punch to it
     connect(this, &LimitedNodeList::nodeAdded, this, &NodeList::startNodeHolePunch);
@@ -110,7 +105,6 @@ NodeList::NodeList(char newOwnerType, unsigned short socketListenPort, unsigned
     packetReceiver.registerListener(PacketType::ICEPing, this, "processICEPingPacket");
     packetReceiver.registerListener(PacketType::DomainServerAddedNode, this, "processDomainServerAddedNode");
     packetReceiver.registerListener(PacketType::DomainServerConnectionToken, this, "processDomainServerConnectionTokenPacket");
-    packetReceiver.registerListener(PacketType::DomainConnectionDenied, &_domainHandler, "processDomainServerConnectionDeniedPacket");
     packetReceiver.registerListener(PacketType::DomainSettings, &_domainHandler, "processSettingsPacketList");
     packetReceiver.registerListener(PacketType::ICEServerPeerInformation, &_domainHandler, "processICEResponsePacket");
     packetReceiver.registerListener(PacketType::DomainServerRequireDTLS, &_domainHandler, "processDTLSRequirementPacket");
@@ -271,26 +265,6 @@ void NodeList::sendDomainServerCheckIn() {
 
         }
 
-        // check if we're missing a keypair we need to verify ourselves with the domain-server
-        auto& accountManager = AccountManager::getInstance();
-        const QUuid& connectionToken = _domainHandler.getConnectionToken();
-
-        // we assume that we're on the same box as the DS if it has the same local address and
-        // it didn't present us with a connection token to use for username signature
-        bool localhostDomain = _domainHandler.getSockAddr().getAddress() == QHostAddress::LocalHost
-            || (_domainHandler.getSockAddr().getAddress() == _localSockAddr.getAddress() && connectionToken.isNull());
-
-        bool requiresUsernameSignature = !_domainHandler.isConnected() && !connectionToken.isNull() && !localhostDomain;
-
-        if (requiresUsernameSignature && !accountManager.getAccountInfo().hasPrivateKey()) {
-            qWarning() << "A keypair is required to present a username signature to the domain-server"
-                << "but no keypair is present. Waiting for keypair generation to complete.";
-            accountManager.generateNewUserKeypair();
-
-            // don't send the check in packet - wait for the keypair first
-            return;
-        }
-
         auto domainPacket = NLPacket::create(domainPacketType);
         
         QDataStream packetStream(domainPacket.get());
@@ -315,15 +289,23 @@ void NodeList::sendDomainServerCheckIn() {
 
         // pack our data to send to the domain-server
         packetStream << _ownerType << _publicSockAddr << _localSockAddr << _nodeTypesOfInterest.toList();
-
-        if (!_domainHandler.isConnected()) {
-            DataServerAccountInfo& accountInfo = accountManager.getAccountInfo();
+        
+        // if this is a connect request, and we can present a username signature, send it along
+        if (!_domainHandler.isConnected() ) {
+            
+            DataServerAccountInfo& accountInfo = AccountManager::getInstance().getAccountInfo();
             packetStream << accountInfo.getUsername();
-
-            // if this is a connect request, and we can present a username signature, send it along
-            if (requiresUsernameSignature && accountManager.getAccountInfo().hasPrivateKey()) {
-                const QByteArray& usernameSignature = accountManager.getAccountInfo().getUsernameSignature(connectionToken);
-                packetStream << usernameSignature;
+            
+            // get connection token from the domain-server
+            const QUuid& connectionToken = _domainHandler.getConnectionToken();
+            
+            if (!connectionToken.isNull()) {
+                
+                const QByteArray& usernameSignature = AccountManager::getInstance().getAccountInfo().getUsernameSignature(connectionToken);
+                
+                if (!usernameSignature.isEmpty()) {
+                    packetStream << usernameSignature;
+                }
             }
         }
 
diff --git a/libraries/networking/src/RSAKeypairGenerator.cpp b/libraries/networking/src/RSAKeypairGenerator.cpp
index a98cf74564..53b9b27cc6 100644
--- a/libraries/networking/src/RSAKeypairGenerator.cpp
+++ b/libraries/networking/src/RSAKeypairGenerator.cpp
@@ -85,12 +85,12 @@ void RSAKeypairGenerator::generateKeypair() {
     // we can cleanup the RSA struct before we continue on
     RSA_free(keyPair);
     
-    _publicKey = QByteArray { reinterpret_cast<char*>(publicKeyDER), publicKeyLength };
-    _privateKey = QByteArray { reinterpret_cast<char*>(privateKeyDER), privateKeyLength };
+    QByteArray publicKeyArray(reinterpret_cast<char*>(publicKeyDER), publicKeyLength);
+    QByteArray privateKeyArray(reinterpret_cast<char*>(privateKeyDER), privateKeyLength);
     
     // cleanup the publicKeyDER and publicKeyDER data
     OPENSSL_free(publicKeyDER);
     OPENSSL_free(privateKeyDER);
     
-    emit generatedKeypair();
+    emit generatedKeypair(publicKeyArray, privateKeyArray);
 }
diff --git a/libraries/networking/src/RSAKeypairGenerator.h b/libraries/networking/src/RSAKeypairGenerator.h
index 36f4a9550b..dd90313625 100644
--- a/libraries/networking/src/RSAKeypairGenerator.h
+++ b/libraries/networking/src/RSAKeypairGenerator.h
@@ -12,31 +12,17 @@
 #ifndef hifi_RSAKeypairGenerator_h
 #define hifi_RSAKeypairGenerator_h
 
-#include <QtCore/QObject>
-#include <QtCore/QUuid>
+#include <qobject.h>
 
 class RSAKeypairGenerator : public QObject {
     Q_OBJECT
 public:
     RSAKeypairGenerator(QObject* parent = 0);
-
-    void setDomainID(const QUuid& domainID) { _domainID = domainID; }
-    const QUuid& getDomainID() const { return _domainID; }
-
-    const QByteArray& getPublicKey() const { return _publicKey; }
-    const QByteArray& getPrivateKey() const { return _privateKey; }
-    
 public slots:
     void generateKeypair();
-
 signals:
     void errorGeneratingKeypair();
-    void generatedKeypair();
-
-private:
-    QUuid _domainID;
-    QByteArray _publicKey;
-    QByteArray _privateKey;
+    void generatedKeypair(const QByteArray& publicKey, const QByteArray& privateKey);
 };
 
-#endif // hifi_RSAKeypairGenerator_h
+#endif // hifi_RSAKeypairGenerator_h
\ No newline at end of file
diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp
index cbe577c14b..88fabf1a5a 100644
--- a/libraries/networking/src/udt/PacketHeaders.cpp
+++ b/libraries/networking/src/udt/PacketHeaders.cpp
@@ -30,7 +30,7 @@ const QSet<PacketType> NON_SOURCED_PACKETS = QSet<PacketType>()
     << PacketType::DomainServerAddedNode << PacketType::DomainServerConnectionToken
     << PacketType::DomainSettingsRequest << PacketType::DomainSettings
     << PacketType::ICEServerPeerInformation << PacketType::ICEServerQuery << PacketType::ICEServerHeartbeat
-    << PacketType::ICEPing << PacketType::ICEPingReply << PacketType::ICEServerHeartbeatDenied
+    << PacketType::ICEPing << PacketType::ICEPingReply
     << PacketType::AssignmentClientStatus << PacketType::StopNode
     << PacketType::DomainServerRemovedNode;
 
@@ -45,8 +45,6 @@ PacketVersion versionForPacketType(PacketType packetType) {
         case PacketType::AvatarData:
         case PacketType::BulkAvatarData:
             return static_cast<PacketVersion>(AvatarMixerPacketVersion::SoftAttachmentSupport);
-        case PacketType::ICEServerHeartbeat:
-            return 18; // ICE Server Heartbeat signing
         default:
             return 17;
     }
diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h
index 7840ecb17e..0f586018db 100644
--- a/libraries/networking/src/udt/PacketHeaders.h
+++ b/libraries/networking/src/udt/PacketHeaders.h
@@ -90,8 +90,7 @@ public:
         DomainServerRemovedNode,
         MessagesData,
         MessagesSubscribe,
-        MessagesUnsubscribe,
-        ICEServerHeartbeatDenied
+        MessagesUnsubscribe
     };
 };