Repository: Tochemey/goakt
Pull request #271 (2024-03-23 13:06:57)

feat: add RemoteStop method

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/Tochemey/goakt/pull/271?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Arsene) Report
Attention: Patch coverage is `83.63636%` with `9 lines` in your changes are missing coverage. Please review.
> Project coverage is 76.11%. Comparing base [(`0b521de`)](https://app.codecov.io/gh/Tochemey/goakt/commit/0b521de8fbb21eead23d82aa3ab9e4d2e36e3a25?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Arsene) to head [(`b95bdea`)](https://app.codecov.io/gh/Tochemey/goakt/pull/271?dropdown=coverage&src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Arsene).

| [Files](https://app.codecov.io/gh/Tochemey/goakt/pull/271?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Arsene) | Patch % | Lines |
|---|---|---|
| [actors/api.go](https://app.codecov.io/gh/Tochemey/goakt/pull/271?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Arsene#diff-YWN0b3JzL2FwaS5nbw==) | 80.95% | [2 Missing and 2 partials :warning: ](https://app.codecov.io/gh/Tochemey/goakt/pull/271?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Arsene) |
| [actors/pid.go](https://app.codecov.io/gh/Tochemey/goakt/pull/271?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Arsene#diff-YWN0b3JzL3BpZC5nbw==) | 85.00% | [2 Missing and 1 partial :warning: ](https://app.codecov.io/gh/Tochemey/goakt/pull/271?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Arsene) |
| [actors/actor\_system.go](https://app.codecov.io/gh/Tochemey/goakt/pull/271?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Arsene#diff-YWN0b3JzL2FjdG9yX3N5c3RlbS5nbw==) | 85.71% | [1 Missing and 1 partial :warning: ](https://app.codecov.io/gh/Tochemey/goakt/pull/271?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Arsene) |

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##             main     #271      +/-   ##
==========================================
+ Coverage   76.00%   76.11%   +0.11%     
==========================================
  Files          34       34              
  Lines        3638     3693      +55     
==========================================
+ Hits         2765     2811      +46     
- Misses        755      760       +5     
- Partials      118      122       +4     
```



</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/Tochemey/goakt/pull/271?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Arsene).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Arsene).


Patch:
diff --git a/README.md b/README.md
index b9dc632..591ba27 100644
--- a/README.md
+++ b/README.md
@@ -235,6 +235,9 @@ creating the actor system. See actor system [options](./actors/option.go). These
 - `RemoteBatchAsk`: to send a bulk messages to a remote actor with replies
 - `RemoteLookup`: to lookup for an actor on a remote host
 - `RemoteReSpawn`: to restarts an actor on a remote machine
+- `RemoteStop`: to stop an actor on a remote machine
+
+These methods can be used from the [API](./actors/api.go) as well as from the [PID](./actors/pid.go) which is the actor reference when an actor is created.
 
 ### Cluster
 
diff --git a/actors/actor_system.go b/actors/actor_system.go
index 2938654..d5d6cf7 100644
--- a/actors/actor_system.go
+++ b/actors/actor_system.go
@@ -971,9 +971,7 @@ func (x *actorSystem) RemoteReSpawn(ctx context.Context, request *connect.Reques
 	}
 
 	actorPath := NewPath(reqCopy.GetName(), NewAddress(x.Name(), reqCopy.GetHost(), int(reqCopy.GetPort())))
-
 	pid, exist := x.actors.Get(actorPath)
-
 	if !exist {
 		logger.Error(ErrAddressNotFound(actorPath.String()).Error())
 		return nil, ErrAddressNotFound(actorPath.String())
@@ -984,10 +982,34 @@ func (x *actorSystem) RemoteReSpawn(ctx context.Context, request *connect.Reques
 	}
 
 	x.actors.Set(pid)
-
 	return connect.NewResponse(new(internalpb.RemoteReSpawnResponse)), nil
 }
 
+// RemoteStop stops an actor on a remote machine
+func (x *actorSystem) RemoteStop(ctx context.Context, request *connect.Request[internalpb.RemoteStopRequest]) (*connect.Response[internalpb.RemoteStopResponse], error) {
+	logger := x.logger
+
+	reqCopy := request.Msg
+
+	if !x.remotingEnabled.Load() {
+		return nil, connect.NewError(connect.CodeFailedPrecondition, ErrRemotingDisabled)
+	}
+
+	actorPath := NewPath(reqCopy.GetName(), NewAddress(x.Name(), reqCopy.GetHost(), int(reqCopy.GetPort())))
+	pid, exist := x.actors.Get(actorPath)
+	if !exist {
+		logger.Error(ErrAddressNotFound(actorPath.String()).Error())
+		return nil, ErrAddressNotFound(actorPath.String())
+	}
+
+	if err := pid.Shutdown(ctx); err != nil {
+		return nil, errors.Wrapf(err, "failed to stop actor=%s", actorPath.String())
+	}
+
+	x.actors.Delete(actorPath)
+	return connect.NewResponse(new(internalpb.RemoteStopResponse)), nil
+}
+
 // handleRemoteAsk handles a synchronous message to another actor and expect a response.
 // This block until a response is received or timed out.
 func (x *actorSystem) handleRemoteAsk(ctx context.Context, to PID, message proto.Message) (response proto.Message, err error) {
diff --git a/actors/api.go b/actors/api.go
index 27f5de8..525e6c2 100644
--- a/actors/api.go
+++ b/actors/api.go
@@ -320,7 +320,7 @@ func RemoteBatchAsk(ctx context.Context, to *goaktpb.Address, messages ...proto.
 	return response.Msg.GetMessages(), nil
 }
 
-// RemoteReSpawn restarts actor address on a remote node.
+// RemoteReSpawn restarts actor on a remote node.
 func RemoteReSpawn(ctx context.Context, host string, port int, name string) error {
 	interceptor, err := otelconnect.NewInterceptor()
 	if err != nil {
@@ -350,3 +350,34 @@ func RemoteReSpawn(ctx context.Context, host string, port int, name string) erro
 
 	return nil
 }
+
+// RemoteStop stops an actor on a remote node.
+func RemoteStop(ctx context.Context, host string, port int, name string) error {
+	interceptor, err := otelconnect.NewInterceptor()
+	if err != nil {
+		return err
+	}
+
+	remoteClient := internalpbconnect.NewRemotingServiceClient(
+		http.NewClient(),
+		http.URL(host, port),
+		connect.WithInterceptors(interceptor),
+		connect.WithGRPC(),
+	)
+
+	request := connect.NewRequest(&internalpb.RemoteStopRequest{
+		Host: host,
+		Port: int32(port),
+		Name: name,
+	})
+
+	if _, err = remoteClient.RemoteStop(ctx, request); err != nil {
+		code := connect.CodeOf(err)
+		if code == connect.CodeNotFound {
+			return nil
+		}
+		return err
+	}
+
+	return nil
+}
diff --git a/actors/errors.go b/actors/errors.go
index b8f5b42..f255a30 100644
--- a/actors/errors.go
+++ b/actors/errors.go
@@ -46,16 +46,14 @@ var (
 	ErrRequestTimeout = errors.New("request timed out")
 	// ErrRemotingDisabled is returned when remoting is not enabled
 	ErrRemotingDisabled = errors.New("remoting is not enabled")
-	// ErrInvalidNode is returned when the given actor system node is not the one requested
-	ErrInvalidNode = connect.NewError(connect.CodeFailedPrecondition, errors.New("invalid actor system node"))
+
 	// ErrAddressNotFound is returned when an actor address is not found
 	ErrAddressNotFound = func(addr string) error {
 		return connect.NewError(connect.CodeNotFound, fmt.Errorf("actor=%s not found", addr))
 	}
 	// ErrRemoteSendFailure is returned when remote message fails
 	ErrRemoteSendFailure = func(err error) error { return connect.NewError(connect.CodeInternal, err) }
-	// ErrInvalidActorInterface is returned when an actor does not implement the Actor interface
-	ErrInvalidActorInterface = errors.New("failed to create instance. Reason: instance does not implement the Actor interface")
+
 	// ErrInvalidInstance is returned when the creation of an actor instance fails
 	ErrInvalidInstance = errors.New("failed to create instance. Reason: invalid instance")
 	// ErrTypeNotFound is returned when an actor type is not found
diff --git a/actors/pid.go b/actors/pid.go
index 27c2616..eac2b70 100644
--- a/actors/pid.go
+++ b/actors/pid.go
@@ -118,6 +118,8 @@ type PID interface {
 	RemoteLookup(ctx context.Context, host string, port int, name string) (addr *goaktpb.Address, err error)
 	// RemoteReSpawn restarts an actor on a remote node.
 	RemoteReSpawn(ctx context.Context, host string, port int, name string) error
+	// RemoteStop stops an actor on a remote node
+	RemoteStop(ctx context.Context, host string, port int, name string) error
 	// Children returns the list of all the children of the given actor that are still alive
 	// or an empty list.
 	Children() []PID
@@ -898,6 +900,36 @@ func (p *pid) RemoteBatchAsk(ctx context.Context, to *goaktpb.Address, messages
 	return response.Msg.GetMessages(), nil
 }
 
+// RemoteStop stops an actor on a remote node
+func (p *pid) RemoteStop(ctx context.Context, host string, port int, name string) error {
+	clientConnectionOptions, err := p.getConnectionOptions()
+	if err != nil {
+		return err
+	}
+
+	remoteClient := internalpbconnect.NewRemotingServiceClient(
+		p.httpClient,
+		http.URL(host, port),
+		clientConnectionOptions...,
+	)
+
+	request := connect.NewRequest(&internalpb.RemoteStopRequest{
+		Host: host,
+		Port: int32(port),
+		Name: name,
+	})
+
+	if _, err = remoteClient.RemoteStop(ctx, request); err != nil {
+		code := connect.CodeOf(err)
+		if code == connect.CodeNotFound {
+			return nil
+		}
+		return err
+	}
+
+	return nil
+}
+
 // RemoteReSpawn restarts an actor on a remote node.
 func (p *pid) RemoteReSpawn(ctx context.Context, host string, port int, name string) error {
 	clientConnectionOptions, err := p.getConnectionOptions()
diff --git a/internal/internalpb/internalpbconnect/remoting.connect.go b/internal/internalpb/internalpbconnect/remoting.connect.go
index e879814..7a56693 100644
--- a/internal/internalpb/internalpbconnect/remoting.connect.go
+++ b/internal/internalpb/internalpbconnect/remoting.connect.go
@@ -51,6 +51,9 @@ const (
 	// RemotingServiceRemoteReSpawnProcedure is the fully-qualified name of the RemotingService's
 	// RemoteReSpawn RPC.
 	RemotingServiceRemoteReSpawnProcedure = "/internalpb.RemotingService/RemoteReSpawn"
+	// RemotingServiceRemoteStopProcedure is the fully-qualified name of the RemotingService's
+	// RemoteStop RPC.
+	RemotingServiceRemoteStopProcedure = "/internalpb.RemotingService/RemoteStop"
 )
 
 // These variables are the protoreflect.Descriptor objects for the RPCs defined in this package.
@@ -62,6 +65,7 @@ var (
 	remotingServiceRemoteBatchTellMethodDescriptor = remotingServiceServiceDescriptor.Methods().ByName("RemoteBatchTell")
 	remotingServiceRemoteBatchAskMethodDescriptor  = remotingServiceServiceDescriptor.Methods().ByName("RemoteBatchAsk")
 	remotingServiceRemoteReSpawnMethodDescriptor   = remotingServiceServiceDescriptor.Methods().ByName("RemoteReSpawn")
+	remotingServiceRemoteStopMethodDescriptor      = remotingServiceServiceDescriptor.Methods().ByName("RemoteStop")
 )
 
 // RemotingServiceClient is a client for the internalpb.RemotingService service.
@@ -80,6 +84,8 @@ type RemotingServiceClient interface {
 	RemoteBatchAsk(context.Context, *connect.Request[internalpb.RemoteBatchAskRequest]) (*connect.Response[internalpb.RemoteBatchAskResponse], error)
 	// RemoteReSpawn restarts an actor on a remote machine
 	RemoteReSpawn(context.Context, *connect.Request[internalpb.RemoteReSpawnRequest]) (*connect.Response[internalpb.RemoteReSpawnResponse], error)
+	// RemoteStop stops an actor on a remote machine
+	RemoteStop(context.Context, *connect.Request[internalpb.RemoteStopRequest]) (*connect.Response[internalpb.RemoteStopResponse], error)
 }
 
 // NewRemotingServiceClient constructs a client for the internalpb.RemotingService service. By
@@ -128,6 +134,12 @@ func NewRemotingServiceClient(httpClient connect.HTTPClient, baseURL string, opt
 			connect.WithSchema(remotingServiceRemoteReSpawnMethodDescriptor),
 			connect.WithClientOptions(opts...),
 		),
+		remoteStop: connect.NewClient[internalpb.RemoteStopRequest, internalpb.RemoteStopResponse](
+			httpClient,
+			baseURL+RemotingServiceRemoteStopProcedure,
+			connect.WithSchema(remotingServiceRemoteStopMethodDescriptor),
+			connect.WithClientOptions(opts...),
+		),
 	}
 }
 
@@ -139,6 +151,7 @@ type remotingServiceClient struct {
 	remoteBatchTell *connect.Client[internalpb.RemoteBatchTellRequest, internalpb.RemoteBatchTellResponse]
 	remoteBatchAsk  *connect.Client[internalpb.RemoteBatchAskRequest, internalpb.RemoteBatchAskResponse]
 	remoteReSpawn   *connect.Client[internalpb.RemoteReSpawnRequest, internalpb.RemoteReSpawnResponse]
+	remoteStop      *connect.Client[internalpb.RemoteStopRequest, internalpb.RemoteStopResponse]
 }
 
 // RemoteAsk calls internalpb.RemotingService.RemoteAsk.
@@ -171,6 +184,11 @@ func (c *remotingServiceClient) RemoteReSpawn(ctx context.Context, req *connect.
 	return c.remoteReSpawn.CallUnary(ctx, req)
 }
 
+// RemoteStop calls internalpb.RemotingService.RemoteStop.
+func (c *remotingServiceClient) RemoteStop(ctx context.Context, req *connect.Request[internalpb.RemoteStopRequest]) (*connect.Response[internalpb.RemoteStopResponse], error) {
+	return c.remoteStop.CallUnary(ctx, req)
+}
+
 // RemotingServiceHandler is an implementation of the internalpb.RemotingService service.
 type RemotingServiceHandler interface {
 	// RemoteAsk is used to send a message to an actor remotely and expect a response immediately.
@@ -187,6 +205,8 @@ type RemotingServiceHandler interface {
 	RemoteBatchAsk(context.Context, *connect.Request[internalpb.RemoteBatchAskRequest]) (*connect.Response[internalpb.RemoteBatchAskResponse], error)
 	// RemoteReSpawn restarts an actor on a remote machine
 	RemoteReSpawn(context.Context, *connect.Request[internalpb.RemoteReSpawnRequest]) (*connect.Response[internalpb.RemoteReSpawnResponse], error)
+	// RemoteStop stops an actor on a remote machine
+	RemoteStop(context.Context, *connect.Request[internalpb.RemoteStopRequest]) (*connect.Response[internalpb.RemoteStopResponse], error)
 }
 
 // NewRemotingServiceHandler builds an HTTP handler from the service implementation. It returns the
@@ -231,6 +251,12 @@ func NewRemotingServiceHandler(svc RemotingServiceHandler, opts ...connect.Handl
 		connect.WithSchema(remotingServiceRemoteReSpawnMethodDescriptor),
 		connect.WithHandlerOptions(opts...),
 	)
+	remotingServiceRemoteStopHandler := connect.NewUnaryHandler(
+		RemotingServiceRemoteStopProcedure,
+		svc.RemoteStop,
+		connect.WithSchema(remotingServiceRemoteStopMethodDescriptor),
+		connect.WithHandlerOptions(opts...),
+	)
 	return "/internalpb.RemotingService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 		switch r.URL.Path {
 		case RemotingServiceRemoteAskProcedure:
@@ -245,6 +271,8 @@ func NewRemotingServiceHandler(svc RemotingServiceHandler, opts ...connect.Handl
 			remotingServiceRemoteBatchAskHandler.ServeHTTP(w, r)
 		case RemotingServiceRemoteReSpawnProcedure:
 			remotingServiceRemoteReSpawnHandler.ServeHTTP(w, r)
+		case RemotingServiceRemoteStopProcedure:
+			remotingServiceRemoteStopHandler.ServeHTTP(w, r)
 		default:
 			http.NotFound(w, r)
 		}
@@ -277,3 +305,7 @@ func (UnimplementedRemotingServiceHandler) RemoteBatchAsk(context.Context, *conn
 func (UnimplementedRemotingServiceHandler) RemoteReSpawn(context.Context, *connect.Request[internalpb.RemoteReSpawnRequest]) (*connect.Response[internalpb.RemoteReSpawnResponse], error) {
 	return nil, connect.NewError(connect.CodeUnimplemented, errors.New("internalpb.RemotingService.RemoteReSpawn is not implemented"))
 }
+
+func (UnimplementedRemotingServiceHandler) RemoteStop(context.Context, *connect.Request[internalpb.RemoteStopRequest]) (*connect.Response[internalpb.RemoteStopResponse], error) {
+	return nil, connect.NewError(connect.CodeUnimplemented, errors.New("internalpb.RemotingService.RemoteStop is not implemented"))
+}
diff --git a/internal/internalpb/remoting.pb.go b/internal/internalpb/remoting.pb.go
index df74e2f..37a3294 100644
--- a/internal/internalpb/remoting.pb.go
+++ b/internal/internalpb/remoting.pb.go
@@ -714,6 +714,110 @@ func (*RemoteReSpawnResponse) Descriptor() ([]byte, []int) {
 	return file_internal_remoting_proto_rawDescGZIP(), []int{12}
 }
 
+type RemoteStopRequest struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// Specifies the remote host address
+	Host string `protobuf:"bytes,1,opt,name=host,proto3" json:"host,omitempty"`
+	// Specifies the remote port
+	Port int32 `protobuf:"varint,2,opt,name=port,proto3" json:"port,omitempty"`
+	// Specifies the actor name
+	Name string `protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty"`
+}
+
+func (x *RemoteStopRequest) Reset() {
+	*x = RemoteStopRequest{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_internal_remoting_proto_msgTypes[13]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *RemoteStopRequest) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*RemoteStopRequest) ProtoMessage() {}
+
+func (x *RemoteStopRequest) ProtoReflect() protoreflect.Message {
+	mi := &file_internal_remoting_proto_msgTypes[13]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use RemoteStopRequest.ProtoReflect.Descriptor instead.
+func (*RemoteStopRequest) Descriptor() ([]byte, []int) {
+	return file_internal_remoting_proto_rawDescGZIP(), []int{13}
+}
+
+func (x *RemoteStopRequest) GetHost() string {
+	if x != nil {
+		return x.Host
+	}
+	return ""
+}
+
+func (x *RemoteStopRequest) GetPort() int32 {
+	if x != nil {
+		return x.Port
+	}
+	return 0
+}
+
+func (x *RemoteStopRequest) GetName() string {
+	if x != nil {
+		return x.Name
+	}
+	return ""
+}
+
+type RemoteStopResponse struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+}
+
+func (x *RemoteStopResponse) Reset() {
+	*x = RemoteStopResponse{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_internal_remoting_proto_msgTypes[14]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *RemoteStopResponse) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*RemoteStopResponse) ProtoMessage() {}
+
+func (x *RemoteStopResponse) ProtoReflect() protoreflect.Message {
+	mi := &file_internal_remoting_proto_msgTypes[14]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use RemoteStopResponse.ProtoReflect.Descriptor instead.
+func (*RemoteStopResponse) Descriptor() ([]byte, []int) {
+	return file_internal_remoting_proto_rawDescGZIP(), []int{14}
+}
+
 var File_internal_remoting_proto protoreflect.FileDescriptor
 
 var file_internal_remoting_proto_rawDesc = []byte{
@@ -791,50 +895,62 @@ var file_internal_remoting_proto_rawDesc = []byte{
 	0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x12, 0x0a, 0x04,
 	0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65,
 	0x22, 0x17, 0x0a, 0x15, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x53, 0x70, 0x61, 0x77,
-	0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0x86, 0x04, 0x0a, 0x0f, 0x52, 0x65,
-	0x6d, 0x6f, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x48, 0x0a,
-	0x09, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x41, 0x73, 0x6b, 0x12, 0x1c, 0x2e, 0x69, 0x6e, 0x74,
-	0x65, 0x72, 0x6e, 0x61, 0x6c, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x41, 0x73,
-	0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72,
-	0x6e, 0x61, 0x6c, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x41, 0x73, 0x6b, 0x52,
-	0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x0a, 0x52, 0x65, 0x6d, 0x6f, 0x74,
-	0x65, 0x54, 0x65, 0x6c, 0x6c, 0x12, 0x1d, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c,
-	0x70, 0x62, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x54, 0x65, 0x6c, 0x6c, 0x52, 0x65, 0x71,
-	0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x70,
-	0x62, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x54, 0x65, 0x6c, 0x6c, 0x52, 0x65, 0x73, 0x70,
-	0x6f, 0x6e, 0x73, 0x65, 0x12, 0x51, 0x0a, 0x0c, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x4c, 0x6f,
-	0x6f, 0x6b, 0x75, 0x70, 0x12, 0x1f, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x70,
-	0x62, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x52, 0x65,
-	0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c,
-	0x70, 0x62, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x52,
-	0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5a, 0x0a, 0x0f, 0x52, 0x65, 0x6d, 0x6f, 0x74,
-	0x65, 0x42, 0x61, 0x74, 0x63, 0x68, 0x54, 0x65, 0x6c, 0x6c, 0x12, 0x22, 0x2e, 0x69, 0x6e, 0x74,
-	0x65, 0x72, 0x6e, 0x61, 0x6c, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x42, 0x61,
-	0x74, 0x63, 0x68, 0x54, 0x65, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23,
-	0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x6d, 0x6f,
-	0x74, 0x65, 0x42, 0x61, 0x74, 0x63, 0x68, 0x54, 0x65, 0x6c, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f,
-	0x6e, 0x73, 0x65, 0x12, 0x57, 0x0a, 0x0e, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x42, 0x61, 0x74,
-	0x63, 0x68, 0x41, 0x73, 0x6b, 0x12, 0x21, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c,
-	0x70, 0x62, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x42, 0x61, 0x74, 0x63, 0x68, 0x41, 0x73,
-	0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72,
-	0x6e, 0x61, 0x6c, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x42, 0x61, 0x74, 0x63,
-	0x68, 0x41, 0x73, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x54, 0x0a, 0x0d,
-	0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x53, 0x70, 0x61, 0x77, 0x6e, 0x12, 0x20, 0x2e,
-	0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x74,
-	0x65, 0x52, 0x65, 0x53, 0x70, 0x61, 0x77, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
-	0x21, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x6d,
-	0x6f, 0x74, 0x65, 0x52, 0x65, 0x53, 0x70, 0x61, 0x77, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
-	0x73, 0x65, 0x42, 0xa3, 0x01, 0x0a, 0x0e, 0x63, 0x6f, 0x6d, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72,
-	0x6e, 0x61, 0x6c, 0x70, 0x62, 0x42, 0x0d, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x69, 0x6e, 0x67, 0x50,
-	0x72, 0x6f, 0x74, 0x6f, 0x48, 0x02, 0x50, 0x01, 0x5a, 0x38, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62,
-	0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x74, 0x6f, 0x63, 0x68, 0x65, 0x6d, 0x65, 0x79, 0x2f, 0x67, 0x6f,
-	0x61, 0x6b, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x69, 0x6e, 0x74,
-	0x65, 0x72, 0x6e, 0x61, 0x6c, 0x70, 0x62, 0x3b, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c,
-	0x70, 0x62, 0xa2, 0x02, 0x03, 0x49, 0x58, 0x58, 0xaa, 0x02, 0x0a, 0x49, 0x6e, 0x74, 0x65, 0x72,
-	0x6e, 0x61, 0x6c, 0x70, 0x62, 0xca, 0x02, 0x0a, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c,
-	0x70, 0x62, 0xe2, 0x02, 0x16, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x70, 0x62, 0x5c,
-	0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x0a, 0x49, 0x6e,
-	0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+	0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x4f, 0x0a, 0x11, 0x52, 0x65, 0x6d,
+	0x6f, 0x74, 0x65, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12,
+	0x0a, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x6f,
+	0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05,
+	0x52, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03,
+	0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x14, 0x0a, 0x12, 0x52, 0x65,
+	0x6d, 0x6f, 0x74, 0x65, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
+	0x32, 0xd3, 0x04, 0x0a, 0x0f, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x72,
+	0x76, 0x69, 0x63, 0x65, 0x12, 0x48, 0x0a, 0x09, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x41, 0x73,
+	0x6b, 0x12, 0x1c, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x70, 0x62, 0x2e, 0x52,
+	0x65, 0x6d, 0x6f, 0x74, 0x65, 0x41, 0x73, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
+	0x1d, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x6d,
+	0x6f, 0x74, 0x65, 0x41, 0x73, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b,
+	0x0a, 0x0a, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x54, 0x65, 0x6c, 0x6c, 0x12, 0x1d, 0x2e, 0x69,
+	0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65,
+	0x54, 0x65, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x69, 0x6e,
+	0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x54,
+	0x65, 0x6c, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x51, 0x0a, 0x0c, 0x52,
+	0x65, 0x6d, 0x6f, 0x74, 0x65, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x12, 0x1f, 0x2e, 0x69, 0x6e,
+	0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x4c,
+	0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x69,
+	0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65,
+	0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5a,
+	0x0a, 0x0f, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x42, 0x61, 0x74, 0x63, 0x68, 0x54, 0x65, 0x6c,
+	0x6c, 0x12, 0x22, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x70, 0x62, 0x2e, 0x52,
+	0x65, 0x6d, 0x6f, 0x74, 0x65, 0x42, 0x61, 0x74, 0x63, 0x68, 0x54, 0x65, 0x6c, 0x6c, 0x52, 0x65,
+	0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c,
+	0x70, 0x62, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x42, 0x61, 0x74, 0x63, 0x68, 0x54, 0x65,
+	0x6c, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x57, 0x0a, 0x0e, 0x52, 0x65,
+	0x6d, 0x6f, 0x74, 0x65, 0x42, 0x61, 0x74, 0x63, 0x68, 0x41, 0x73, 0x6b, 0x12, 0x21, 0x2e, 0x69,
+	0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65,
+	0x42, 0x61, 0x74, 0x63, 0x68, 0x41, 0x73, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
+	0x22, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x6d,
+	0x6f, 0x74, 0x65, 0x42, 0x61, 0x74, 0x63, 0x68, 0x41, 0x73, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f,
+	0x6e, 0x73, 0x65, 0x12, 0x54, 0x0a, 0x0d, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x53,
+	0x70, 0x61, 0x77, 0x6e, 0x12, 0x20, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x70,
+	0x62, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x53, 0x70, 0x61, 0x77, 0x6e, 0x52,
+	0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61,
+	0x6c, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x53, 0x70, 0x61, 0x77,
+	0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x0a, 0x52, 0x65, 0x6d,
+	0x6f, 0x74, 0x65, 0x53, 0x74, 0x6f, 0x70, 0x12, 0x1d, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e,
+	0x61, 0x6c, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x53, 0x74, 0x6f, 0x70, 0x52,
+	0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61,
+	0x6c, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65,
+	0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0xa3, 0x01, 0x0a, 0x0e, 0x63, 0x6f, 0x6d, 0x2e, 0x69,
+	0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x70, 0x62, 0x42, 0x0d, 0x52, 0x65, 0x6d, 0x6f, 0x74,
+	0x69, 0x6e, 0x67, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x48, 0x02, 0x50, 0x01, 0x5a, 0x38, 0x67, 0x69,
+	0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x74, 0x6f, 0x63, 0x68, 0x65, 0x6d, 0x65,
+	0x79, 0x2f, 0x67, 0x6f, 0x61, 0x6b, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c,
+	0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x70, 0x62, 0x3b, 0x69, 0x6e, 0x74, 0x65,
+	0x72, 0x6e, 0x61, 0x6c, 0x70, 0x62, 0xa2, 0x02, 0x03, 0x49, 0x58, 0x58, 0xaa, 0x02, 0x0a, 0x49,
+	0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x70, 0x62, 0xca, 0x02, 0x0a, 0x49, 0x6e, 0x74, 0x65,
+	0x72, 0x6e, 0x61, 0x6c, 0x70, 0x62, 0xe2, 0x02, 0x16, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61,
+	0x6c, 0x70, 0x62, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea,
+	0x02, 0x0a, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72,
+	0x6f, 0x74, 0x6f, 0x33,
 }
 
 var (
@@ -849,7 +965,7 @@ func file_internal_remoting_proto_rawDescGZIP() []byte {
 	return file_internal_remoting_proto_rawDescData
 }
 
-var file_internal_remoting_proto_msgTypes = make([]protoimpl.MessageInfo, 13)
+var file_internal_remoting_proto_msgTypes = make([]protoimpl.MessageInfo, 15)
 var file_internal_remoting_proto_goTypes = []interface{}{
 	(*RemoteAskRequest)(nil),        // 0: internalpb.RemoteAskRequest
 	(*RemoteAskResponse)(nil),       // 1: internalpb.RemoteAskResponse
@@ -864,38 +980,42 @@ var file_internal_remoting_proto_goTypes = []interface{}{
 	(*RemoteBatchAskResponse)(nil),  // 10: internalpb.RemoteBatchAskResponse
 	(*RemoteReSpawnRequest)(nil),    // 11: internalpb.RemoteReSpawnRequest
 	(*RemoteReSpawnResponse)(nil),   // 12: internalpb.RemoteReSpawnResponse
-	(*anypb.Any)(nil),               // 13: google.protobuf.Any
-	(*goaktpb.Address)(nil),         // 14: goaktpb.Address
+	(*RemoteStopRequest)(nil),       // 13: internalpb.RemoteStopRequest
+	(*RemoteStopResponse)(nil),      // 14: internalpb.RemoteStopResponse
+	(*anypb.Any)(nil),               // 15: google.protobuf.Any
+	(*goaktpb.Address)(nil),         // 16: goaktpb.Address
 }
 var file_internal_remoting_proto_depIdxs = []int32{
 	6,  // 0: internalpb.RemoteAskRequest.remote_message:type_name -> internalpb.RemoteMessage
-	13, // 1: internalpb.RemoteAskResponse.message:type_name -> google.protobuf.Any
+	15, // 1: internalpb.RemoteAskResponse.message:type_name -> google.protobuf.Any
 	6,  // 2: internalpb.RemoteTellRequest.remote_message:type_name -> internalpb.RemoteMessage
-	14, // 3: internalpb.RemoteLookupResponse.address:type_name -> goaktpb.Address
-	14, // 4: internalpb.RemoteMessage.sender:type_name -> goaktpb.Address
-	14, // 5: internalpb.RemoteMessage.receiver:type_name -> goaktpb.Address
-	13, // 6: internalpb.RemoteMessage.message:type_name -> google.protobuf.Any
-	13, // 7: internalpb.RemoteBatchTellRequest.messages:type_name -> google.protobuf.Any
-	14, // 8: internalpb.RemoteBatchTellRequest.sender:type_name -> goaktpb.Address
-	14, // 9: internalpb.RemoteBatchTellRequest.receiver:type_name -> goaktpb.Address
-	13, // 10: internalpb.RemoteBatchAskRequest.messages:type_name -> google.protobuf.Any
-	14, // 11: internalpb.RemoteBatchAskRequest.sender:type_name -> goaktpb.Address
-	14, // 12: internalpb.RemoteBatchAskRequest.receiver:type_name -> goaktpb.Address
-	13, // 13: internalpb.RemoteBatchAskResponse.messages:type_name -> google.protobuf.Any
+	16, // 3: internalpb.RemoteLookupResponse.address:type_name -> goaktpb.Address
+	16, // 4: internalpb.RemoteMessage.sender:type_name -> goaktpb.Address
+	16, // 5: internalpb.RemoteMessage.receiver:type_name -> goaktpb.Address
+	15, // 6: internalpb.RemoteMessage.message:type_name -> google.protobuf.Any
+	15, // 7: internalpb.RemoteBatchTellRequest.messages:type_name -> google.protobuf.Any
+	16, // 8: internalpb.RemoteBatchTellRequest.sender:type_name -> goaktpb.Address
+	16, // 9: internalpb.RemoteBatchTellRequest.receiver:type_name -> goaktpb.Address
+	15, // 10: internalpb.RemoteBatchAskRequest.messages:type_name -> google.protobuf.Any
+	16, // 11: internalpb.RemoteBatchAskRequest.sender:type_name -> goaktpb.Address
+	16, // 12: internalpb.RemoteBatchAskRequest.receiver:type_name -> goaktpb.Address
+	15, // 13: internalpb.RemoteBatchAskResponse.messages:type_name -> google.protobuf.Any
 	0,  // 14: internalpb.RemotingService.RemoteAsk:input_type -> internalpb.RemoteAskRequest
 	2,  // 15: internalpb.RemotingService.RemoteTell:input_type -> internalpb.RemoteTellRequest
 	4,  // 16: internalpb.RemotingService.RemoteLookup:input_type -> internalpb.RemoteLookupRequest
 	7,  // 17: internalpb.RemotingService.RemoteBatchTell:input_type -> internalpb.RemoteBatchTellRequest
 	9,  // 18: internalpb.RemotingService.RemoteBatchAsk:input_type -> internalpb.RemoteBatchAskRequest
 	11, // 19: internalpb.RemotingService.RemoteReSpawn:input_type -> internalpb.RemoteReSpawnRequest
-	1,  // 20: internalpb.RemotingService.RemoteAsk:output_type -> internalpb.RemoteAskResponse
-	3,  // 21: internalpb.RemotingService.RemoteTell:output_type -> internalpb.RemoteTellResponse
-	5,  // 22: internalpb.RemotingService.RemoteLookup:output_type -> internalpb.RemoteLookupResponse
-	8,  // 23: internalpb.RemotingService.RemoteBatchTell:output_type -> internalpb.RemoteBatchTellResponse
-	10, // 24: internalpb.RemotingService.RemoteBatchAsk:output_type -> internalpb.RemoteBatchAskResponse
-	12, // 25: internalpb.RemotingService.RemoteReSpawn:output_type -> internalpb.RemoteReSpawnResponse
-	20, // [20:26] is the sub-list for method output_type
-	14, // [14:20] is the sub-list for method input_type
+	13, // 20: internalpb.RemotingService.RemoteStop:input_type -> internalpb.RemoteStopRequest
+	1,  // 21: internalpb.RemotingService.RemoteAsk:output_type -> internalpb.RemoteAskResponse
+	3,  // 22: internalpb.RemotingService.RemoteTell:output_type -> internalpb.RemoteTellResponse
+	5,  // 23: internalpb.RemotingService.RemoteLookup:output_type -> internalpb.RemoteLookupResponse
+	8,  // 24: internalpb.RemotingService.RemoteBatchTell:output_type -> internalpb.RemoteBatchTellResponse
+	10, // 25: internalpb.RemotingService.RemoteBatchAsk:output_type -> internalpb.RemoteBatchAskResponse
+	12, // 26: internalpb.RemotingService.RemoteReSpawn:output_type -> internalpb.RemoteReSpawnResponse
+	14, // 27: internalpb.RemotingService.RemoteStop:output_type -> internalpb.RemoteStopResponse
+	21, // [21:28] is the sub-list for method output_type
+	14, // [14:21] is the sub-list for method input_type
 	14, // [14:14] is the sub-list for extension type_name
 	14, // [14:14] is the sub-list for extension extendee
 	0,  // [0:14] is the sub-list for field type_name
@@ -1063,6 +1183,30 @@ func file_internal_remoting_proto_init() {
 				return nil
 			}
 		}
+		file_internal_remoting_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*RemoteStopRequest); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_internal_remoting_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*RemoteStopResponse); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
 	}
 	type x struct{}
 	out := protoimpl.TypeBuilder{
@@ -1070,7 +1214,7 @@ func file_internal_remoting_proto_init() {
 			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
 			RawDescriptor: file_internal_remoting_proto_rawDesc,
 			NumEnums:      0,
-			NumMessages:   13,
+			NumMessages:   15,
 			NumExtensions: 0,
 			NumServices:   1,
 		},
diff --git a/protos/internal/remoting.proto b/protos/internal/remoting.proto
index 42defb0..ae9e519 100644
--- a/protos/internal/remoting.proto
+++ b/protos/internal/remoting.proto
@@ -26,6 +26,8 @@ service RemotingService {
   rpc RemoteBatchAsk(RemoteBatchAskRequest) returns (RemoteBatchAskResponse);
   // RemoteReSpawn restarts an actor on a remote machine
   rpc RemoteReSpawn(RemoteReSpawnRequest) returns (RemoteReSpawnResponse);
+  // RemoteStop stops an actor on a remote machine
+  rpc RemoteStop(RemoteStopRequest) returns (RemoteStopResponse);
 }
 
 // RemoteAsk is used to send a message to an actor remotely and expect a response
@@ -111,3 +113,14 @@ message RemoteReSpawnRequest {
 }
 
 message RemoteReSpawnResponse {}
+
+message RemoteStopRequest {
+  // Specifies the remote host address
+  string host = 1;
+  // Specifies the remote port
+  int32 port = 2;
+  // Specifies the actor name
+  string name = 3;
+}
+
+message RemoteStopResponse {}


Test patch:
diff --git a/actors/api_test.go b/actors/api_test.go
index 4f38640..605aa49 100644
--- a/actors/api_test.go
+++ b/actors/api_test.go
@@ -1610,3 +1610,94 @@ func TestAPIRemoteReSpawn(t *testing.T) {
 		assert.NoError(t, err)
 	})
 }
+
+func TestAPIRemoteStop(t *testing.T) {
+	t.Run("When remoting is not enabled", func(t *testing.T) {
+		// create the context
+		ctx := context.TODO()
+		// define the logger to use
+		logger := log.New(log.DebugLevel, os.Stdout)
+		// generate the remoting port
+		nodePorts := dynaport.Get(1)
+		remotingPort := nodePorts[0]
+		host := "0.0.0.0"
+
+		// create the actor system
+		sys, err := NewActorSystem("test",
+			WithLogger(logger),
+			WithPassivationDisabled(),
+			WithRemoting(host, int32(remotingPort)),
+		)
+		// assert there are no error
+		require.NoError(t, err)
+
+		// start the actor system
+		err = sys.Start(ctx)
+		assert.NoError(t, err)
+
+		time.Sleep(time.Second)
+
+		// let us disable remoting
+		actorsSystem := sys.(*actorSystem)
+		actorsSystem.remotingEnabled.Store(false)
+
+		// create a test actor
+		actorName := "test"
+		// get the address of the actor
+		err = RemoteStop(ctx, host, remotingPort, actorName)
+		require.Error(t, err)
+		require.EqualError(t, err, "failed_precondition: remoting is not enabled")
+
+		t.Cleanup(func() {
+			assert.NoError(t, sys.Stop(ctx))
+		})
+	})
+	t.Run("When remoting is enabled", func(t *testing.T) {
+		// create the context
+		ctx := context.TODO()
+		// define the logger to use
+		logger := log.New(log.DebugLevel, os.Stdout)
+		// generate the remoting port
+		nodePorts := dynaport.Get(1)
+		remotingPort := nodePorts[0]
+		host := "0.0.0.0"
+
+		// create the actor system
+		sys, err := NewActorSystem("test",
+			WithLogger(logger),
+			WithPassivationDisabled(),
+			WithRemoting(host, int32(remotingPort)),
+		)
+		// assert there are no error
+		require.NoError(t, err)
+
+		// start the actor system
+		err = sys.Start(ctx)
+		assert.NoError(t, err)
+
+		time.Sleep(time.Second)
+
+		// create a test actor
+		actorName := "test"
+		actor := newTestActor()
+		actorRef, err := sys.Spawn(ctx, actorName, actor)
+		require.NoError(t, err)
+		assert.NotNil(t, actorRef)
+
+		// assert the actor restart count
+		pid := actorRef.(*pid)
+		assert.Zero(t, pid.restartCount.Load())
+
+		// get the address of the actor
+		err = RemoteStop(ctx, host, remotingPort, actorName)
+		require.NoError(t, err)
+
+		assert.Empty(t, sys.Actors())
+
+		// stop the actor after some time
+		time.Sleep(time.Second)
+
+		err = sys.Stop(ctx)
+		assert.NoError(t, err)
+	})
+}
diff --git a/actors/pid_test.go b/actors/pid_test.go
index 9586de8..5c5e31a 100644
--- a/actors/pid_test.go
+++ b/actors/pid_test.go
@@ -1461,7 +1461,6 @@ func TestRemoteReSpawn(t *testing.T) {
 		require.NoError(t, err)
 		assert.NotNil(t, actorRef1)
 
-		// let us lookup actor two
 		actorName2 := "Exchange2"
 		err = actorRef1.RemoteReSpawn(ctx, host, remotingPort, actorName2)
 		require.NoError(t, err)
@@ -1498,7 +1497,6 @@ func TestRemoteReSpawn(t *testing.T) {
 		require.NoError(t, err)
 		assert.NotNil(t, actorRef1)
 
-		// let us lookup actor two
 		actorName2 := "Exchange2"
 		err = actorRef1.RemoteReSpawn(ctx, host, remotingPort, actorName2)
 		require.Error(t, err)
@@ -1508,3 +1506,124 @@ func TestRemoteReSpawn(t *testing.T) {
 		})
 	})
 }
+
+func TestRemoteStop(t *testing.T) {
+	t.Run("With actor address not found", func(t *testing.T) {
+		// create the context
+		ctx := context.TODO()
+		// define the logger to use
+		logger := log.New(log.DebugLevel, os.Stdout)
+		// generate the remoting port
+		nodePorts := dynaport.Get(1)
+		remotingPort := nodePorts[0]
+		host := "localhost"
+
+		// create the actor system
+		sys, err := NewActorSystem("test",
+			WithLogger(logger),
+			WithPassivationDisabled(),
+			WithRemoting(host, int32(remotingPort)),
+		)
+		// assert there are no error
+		require.NoError(t, err)
+
+		// start the actor system
+		err = sys.Start(ctx)
+		assert.NoError(t, err)
+
+		// create an exchanger 1
+		actorName1 := "Exchange1"
+		actorRef1, err := sys.Spawn(ctx, actorName1, &exchanger{})
+
+		require.NoError(t, err)
+		assert.NotNil(t, actorRef1)
+
+		// let us lookup actor two
+		actorName2 := "Exchange2"
+		err = actorRef1.RemoteStop(ctx, host, remotingPort, actorName2)
+		require.NoError(t, err)
+
+		t.Cleanup(func() {
+			assert.NoError(t, sys.Stop(ctx))
+		})
+	})
+	t.Run("With remoting enabled and actor started", func(t *testing.T) {
+		// create the context
+		ctx := context.TODO()
+		// define the logger to use
+		logger := log.New(log.DebugLevel, os.Stdout)
+		// generate the remoting port
+		nodePorts := dynaport.Get(1)
+		remotingPort := nodePorts[0]
+		host := "localhost"
+
+		// create the actor system
+		sys, err := NewActorSystem("test",
+			WithLogger(logger),
+			WithPassivationDisabled(),
+			WithRemoting(host, int32(remotingPort)),
+		)
+		// assert there are no error
+		require.NoError(t, err)
+
+		// start the actor system
+		err = sys.Start(ctx)
+		assert.NoError(t, err)
+
+		// create an exchanger 1
+		actorName1 := "Exchange1"
+		actorRef1, err := sys.Spawn(ctx, actorName1, &exchanger{})
+		require.NoError(t, err)
+		assert.NotNil(t, actorRef1)
+
+		// create an exchanger 1
+		actorName2 := "Exchange2"
+		actorRef2, err := sys.Spawn(ctx, actorName2, &exchanger{})
+		require.NoError(t, err)
+		assert.NotNil(t, actorRef2)
+
+		err = actorRef1.RemoteStop(ctx, host, remotingPort, actorName2)
+		require.NoError(t, err)
+
+		t.Cleanup(func() {
+			assert.NoError(t, sys.Stop(ctx))
+		})
+	})
+	t.Run("With remoting enabled and actor not started", func(t *testing.T) {
+		// create the context
+		ctx := context.TODO()
+		// define the logger to use
+		logger := log.New(log.DebugLevel, os.Stdout)
+		// generate the remoting port
+		nodePorts := dynaport.Get(1)
+		remotingPort := nodePorts[0]
+		host := "localhost"
+
+		// create the actor system
+		sys, err := NewActorSystem("test",
+			WithLogger(logger),
+			WithPassivationDisabled(),
+			WithRemoting(host, int32(remotingPort)),
+		)
+		// assert there are no error
+		require.NoError(t, err)
+
+		// start the actor system
+		err = sys.Start(ctx)
+		assert.NoError(t, err)
+
+		// create an exchanger 1
+		actorName1 := "Exchange1"
+		actorRef1, err := sys.Spawn(ctx, actorName1, &exchanger{})
+		require.NoError(t, err)
+		assert.NotNil(t, actorRef1)
+
+		actorName2 := "Exchange2"
+		err = actorRef1.RemoteStop(ctx, host, remotingPort, actorName2)
+		require.NoError(t, err)
+
+		t.Cleanup(func() {
+			assert.NoError(t, sys.Stop(ctx))
+		})
+	})
+}