diff --git a/src/core/hle/kernel/client_port.cpp b/src/core/hle/kernel/client_port.cpp
index 5ee7679eb..9a9cd4bfd 100644
--- a/src/core/hle/kernel/client_port.cpp
+++ b/src/core/hle/kernel/client_port.cpp
@@ -7,16 +7,39 @@
 #include "core/hle/kernel/kernel.h"
 #include "core/hle/kernel/server_port.h"
 #include "core/hle/kernel/server_session.h"
+#include "core/hle/service/service.h"
 
 namespace Kernel {
 
 ClientPort::ClientPort() {}
 ClientPort::~ClientPort() {}
 
+Kernel::SharedPtr<ClientPort> ClientPort::CreateForHLE(u32 max_sessions, std::unique_ptr<Service::Interface> hle_interface) {
+    SharedPtr<ClientPort> client_port(new ClientPort);
+    client_port->max_sessions = max_sessions;
+    client_port->active_sessions = 0;
+    client_port->name = hle_interface->GetPortName();
+    client_port->hle_interface = std::move(hle_interface);
+
+    return client_port;
+}
+
 void ClientPort::AddWaitingSession(SharedPtr<ServerSession> server_session) {
+    // A port that has an associated HLE interface doesn't have a server port.
+    if (hle_interface != nullptr)
+        return;
+
     server_port->pending_sessions.push_back(server_session);
     // Wake the threads waiting on the ServerPort
     server_port->WakeupAllWaitingThreads();
 }
 
+ResultCode ClientPort::HandleSyncRequest() {
+    // Forward the request to the associated HLE interface if it exists
+    if (hle_interface != nullptr)
+        return hle_interface->HandleSyncRequest();
+
+    return RESULT_SUCCESS;
+}
+
 } // namespace
diff --git a/src/core/hle/kernel/client_port.h b/src/core/hle/kernel/client_port.h
index eb0882870..ee65606ba 100644
--- a/src/core/hle/kernel/client_port.h
+++ b/src/core/hle/kernel/client_port.h
@@ -5,18 +5,31 @@
 #pragma once
 
 #include <string>
+#include <memory>
 #include "common/common_types.h"
 #include "core/hle/kernel/kernel.h"
 
+namespace Service {
+class Interface;
+}
+
 namespace Kernel {
 
 class ServerPort;
 class ServerSession;
 
-class ClientPort : public Object {
+class ClientPort final : public Object {
 public:
     friend class ServerPort;
 
+    /**
+     * Creates a serverless ClientPort that represents a bridge between the HLE implementation of a service/port and the emulated application.
+     * @param max_sessions Maximum number of sessions that this port is able to handle concurrently.
+     * @param hle_interface Interface object that implements the commands of the service.
+     * @returns ClientPort for the given HLE interface.
+     */
+    static Kernel::SharedPtr<ClientPort> CreateForHLE(u32 max_sessions, std::unique_ptr<Service::Interface> hle_interface);
+
     /**
      * Adds the specified server session to the queue of pending sessions of the associated ServerPort
      * @param server_session Server session to add to the queue
@@ -25,10 +38,9 @@ public:
 
     /**
      * Handle a sync request from the emulated application.
-     * Only HLE services should override this function.
      * @returns ResultCode from the operation.
      */
-    virtual ResultCode HandleSyncRequest() { return RESULT_SUCCESS; }
+    ResultCode HandleSyncRequest();
 
     std::string GetTypeName() const override { return "ClientPort"; }
     std::string GetName() const override { return name; }
@@ -38,12 +50,13 @@ public:
         return HANDLE_TYPE;
     }
 
-    SharedPtr<ServerPort> server_port; ///< ServerPort associated with this client port.
-    u32 max_sessions;    ///< Maximum number of simultaneous sessions the port can have
-    u32 active_sessions; ///< Number of currently open sessions to this port
-    std::string name;    ///< Name of client port (optional)
+    SharedPtr<ServerPort> server_port = nullptr;                 ///< ServerPort associated with this client port.
+    u32 max_sessions;                                            ///< Maximum number of simultaneous sessions the port can have
+    u32 active_sessions;                                         ///< Number of currently open sessions to this port
+    std::string name;                                            ///< Name of client port (optional)
+    std::unique_ptr<Service::Interface> hle_interface = nullptr; ///< HLE implementation of this port's request handler
 
-protected:
+private:
     ClientPort();
     ~ClientPort() override;
 };
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index f51a042ff..abfc1806b 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -97,17 +97,13 @@ void Interface::Register(const FunctionInfo* functions, size_t n) {
 // Module interface
 
 static void AddNamedPort(Interface* interface_) {
-    interface_->name = interface_->GetPortName();
-    interface_->active_sessions = 0;
-    interface_->max_sessions = interface_->GetMaxSessions();
-    g_kernel_named_ports.emplace(interface_->GetPortName(), interface_);
+    auto client_port = Kernel::ClientPort::CreateForHLE(interface_->GetMaxSessions(), std::unique_ptr<Interface>(interface_));
+    g_kernel_named_ports.emplace(interface_->GetPortName(), client_port);
 }
 
 void AddService(Interface* interface_) {
-    interface_->name = interface_->GetPortName();
-    interface_->active_sessions = 0;
-    interface_->max_sessions = interface_->GetMaxSessions();
-    g_srv_services.emplace(interface_->GetPortName(), interface_);
+    auto client_port = Kernel::ClientPort::CreateForHLE(interface_->GetMaxSessions(), std::unique_ptr<Interface>(interface_));
+    g_srv_services.emplace(interface_->GetPortName(), client_port);
 }
 
 /// Initialize ServiceManager
diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h
index 8df968b2e..b22caca07 100644
--- a/src/core/hle/service/service.h
+++ b/src/core/hle/service/service.h
@@ -22,18 +22,16 @@ static const int kMaxPortSize = 8; ///< Maximum size of a port name (8 character
 static const u32 DefaultMaxSessions = 10; ///< Arbitrary default number of maximum connections to an HLE port
 
 /// Interface to a CTROS service
-class Interface : public Kernel::ClientPort {
-    // TODO(yuriks): An "Interface" being a Kernel::Object is mostly non-sense. Interface should be
-    // just something that encapsulates a session and acts as a helper to implement service
-    // processes.
+class Interface {
 public:
-    std::string GetName() const override {
+    std::string GetName() const {
         return GetPortName();
     }
 
     virtual void SetVersion(u32 raw_version) {
         version.raw = raw_version;
     }
+    virtual ~Interface() {}
 
     /**
      * Gets the maximum allowed number of sessions that can be connected to this port at the same time.
@@ -42,8 +40,6 @@ public:
      */
     virtual u32 GetMaxSessions() const { return DefaultMaxSessions; }
 
-    void AddWaitingSession(Kernel::SharedPtr<Kernel::ServerSession> server_session) override { }
-
     typedef void (*Function)(Interface*);
 
     struct FunctionInfo {
@@ -60,7 +56,7 @@ public:
         return "[UNKNOWN SERVICE PORT]";
     }
 
-    ResultCode HandleSyncRequest() override;
+    ResultCode HandleSyncRequest();
 
 protected:
     /**