diff --git a/impl/grpc/pom.xml b/impl/grpc/pom.xml
new file mode 100644
index 00000000..f6583ac9
--- /dev/null
+++ b/impl/grpc/pom.xml
@@ -0,0 +1,37 @@
+
+ 4.0.0
+
+ io.serverlessworkflow
+ serverlessworkflow-impl
+ 8.0.0-SNAPSHOT
+
+ serverlessworkflow-impl-grpc
+ Serverless Workflow :: Impl :: gRPC
+
+
+
+ io.serverlessworkflow
+ serverlessworkflow-impl-core
+
+
+ io.grpc
+ grpc-stub
+
+
+ com.google.protobuf
+ protobuf-java-util
+
+
+ io.serverlessworkflow
+ serverlessworkflow-impl-json
+
+
+ com.github.os72
+ protoc-jar
+
+
+ io.grpc
+ grpc-protobuf
+
+
+
\ No newline at end of file
diff --git a/impl/grpc/src/main/java/io/serverlessworkflow/impl/executors/grpc/CollectionStreamObserver.java b/impl/grpc/src/main/java/io/serverlessworkflow/impl/executors/grpc/CollectionStreamObserver.java
new file mode 100644
index 00000000..cd1c51ef
--- /dev/null
+++ b/impl/grpc/src/main/java/io/serverlessworkflow/impl/executors/grpc/CollectionStreamObserver.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2020-Present The Serverless Workflow Specification Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.serverlessworkflow.impl.executors.grpc;
+
+import com.google.protobuf.Message;
+import io.grpc.stub.StreamObserver;
+import io.serverlessworkflow.impl.WorkflowModel;
+import io.serverlessworkflow.impl.WorkflowModelCollection;
+import io.serverlessworkflow.impl.WorkflowModelFactory;
+import java.util.concurrent.CompletableFuture;
+
+class CollectionStreamObserver implements StreamObserver {
+ private final WorkflowModelCollection modelCollection;
+ private final WorkflowModelFactory modelFactory;
+ private final CompletableFuture future;
+
+ public CollectionStreamObserver(WorkflowModelFactory modelFactory) {
+ this.modelCollection = modelFactory.createCollection();
+ this.modelFactory = modelFactory;
+ this.future = new CompletableFuture<>();
+ }
+
+ @Override
+ public void onNext(Message value) {
+ modelCollection.add(ProtobufMessageUtils.convert(value, modelFactory));
+ }
+
+ @Override
+ public void onError(Throwable t) {
+ future.completeExceptionally(t);
+ }
+
+ @Override
+ public void onCompleted() {
+ future.complete(modelCollection);
+ }
+
+ public CompletableFuture future() {
+ return future;
+ }
+}
diff --git a/impl/grpc/src/main/java/io/serverlessworkflow/impl/executors/grpc/FileDescriptorContext.java b/impl/grpc/src/main/java/io/serverlessworkflow/impl/executors/grpc/FileDescriptorContext.java
new file mode 100644
index 00000000..158fab51
--- /dev/null
+++ b/impl/grpc/src/main/java/io/serverlessworkflow/impl/executors/grpc/FileDescriptorContext.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2020-Present The Serverless Workflow Specification Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.serverlessworkflow.impl.executors.grpc;
+
+import com.google.protobuf.DescriptorProtos;
+
+record FileDescriptorContext(
+ DescriptorProtos.FileDescriptorSet fileDescriptorSet, String inputProto) {}
diff --git a/impl/grpc/src/main/java/io/serverlessworkflow/impl/executors/grpc/FileDescriptorReader.java b/impl/grpc/src/main/java/io/serverlessworkflow/impl/executors/grpc/FileDescriptorReader.java
new file mode 100644
index 00000000..f2123e0e
--- /dev/null
+++ b/impl/grpc/src/main/java/io/serverlessworkflow/impl/executors/grpc/FileDescriptorReader.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright 2020-Present The Serverless Workflow Specification Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.serverlessworkflow.impl.executors.grpc;
+
+import com.github.os72.protocjar.Protoc;
+import com.google.protobuf.DescriptorProtos;
+import io.serverlessworkflow.impl.resources.ExternalResourceHandler;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UncheckedIOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
+import java.util.Optional;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+class FileDescriptorReader {
+
+ private static final Logger logger = LoggerFactory.getLogger(FileDescriptorReader.class);
+
+ static FileDescriptorContext readDescriptor(ExternalResourceHandler externalResourceHandler) {
+ Path grpcDir =
+ tryCreateTempGrpcDir()
+ .orElseThrow(
+ () -> new IllegalStateException("Could not create temporary gRPC directory"));
+
+ try (InputStream inputStream = externalResourceHandler.open()) {
+
+ Path protoFile = grpcDir.resolve(externalResourceHandler.name());
+ if (!Files.exists(protoFile)) {
+ Files.createDirectories(protoFile);
+ }
+
+ Files.copy(inputStream, protoFile, StandardCopyOption.REPLACE_EXISTING);
+
+ Path descriptorOutput = grpcDir.resolve("descriptor.protobin");
+
+ try {
+
+ generateFileDescriptor(grpcDir, protoFile, descriptorOutput);
+
+ DescriptorProtos.FileDescriptorSet fileDescriptorSet =
+ DescriptorProtos.FileDescriptorSet.newBuilder()
+ .mergeFrom(Files.readAllBytes(descriptorOutput))
+ .build();
+
+ return new FileDescriptorContext(fileDescriptorSet, externalResourceHandler.name());
+
+ } catch (IOException e) {
+ throw new UncheckedIOException(
+ "Unable to read external resource handler: " + externalResourceHandler.name(), e);
+ }
+ } catch (IOException e) {
+ throw new UncheckedIOException("Unable to read descriptor file", e);
+ }
+ }
+
+ private static Optional tryCreateTempGrpcDir() {
+ try {
+ return Optional.of(Files.createTempDirectory("serverless-workflow-"));
+ } catch (IOException e) {
+ throw new UncheckedIOException("Error while creating temporary gRPC directory", e);
+ }
+ }
+
+ /**
+ * Calls protoc binary with --descriptor_set_out= option set. First attempts to use
+ * the embedded protoc from protoc-jar library. If that fails with FileNotFoundException
+ * (unsupported architecture), falls back to using the system's installed protoc via
+ * ProcessBuilder.
+ *
+ * @param grpcDir a temporary directory
+ * @param protoFile the .proto file used by protoc to generate the file descriptor
+ * @param descriptorOutput the output directory where the descriptor file will be generated
+ */
+ private static void generateFileDescriptor(Path grpcDir, Path protoFile, Path descriptorOutput) {
+ String[] protocArgs =
+ new String[] {
+ "--include_imports",
+ "--descriptor_set_out=" + descriptorOutput.toAbsolutePath(),
+ "-I",
+ grpcDir.toAbsolutePath().toString(),
+ protoFile.toAbsolutePath().toString()
+ };
+
+ try {
+ // First attempt: use protoc-jar library
+ int status = Protoc.runProtoc(protocArgs);
+
+ if (status != 0) {
+ throw new RuntimeException(
+ "Unable to generate file descriptor, 'protoc' execution failed with status " + status);
+ }
+ } catch (FileNotFoundException e) {
+ // Fallback: try using system's installed protoc
+ logger.warn(
+ "Protoc binary not available for this architecture via protoc-jar. "
+ + "Attempting to use system-installed protoc...");
+ generateFileDescriptorWithProcessBuilder(protocArgs);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ throw new RuntimeException("Unable to generate file descriptor", e);
+ } catch (IOException e) {
+ throw new UncheckedIOException("Unable to generate file descriptor", e);
+ }
+ }
+
+ /**
+ * Fallback method to generate file descriptor using system's installed protoc via ProcessBuilder.
+ *
+ * @param protocArgs the arguments to pass to protoc command
+ */
+ private static void generateFileDescriptorWithProcessBuilder(String[] protocArgs) {
+ try {
+ String[] command = new String[protocArgs.length + 1];
+ command[0] = "protoc";
+ System.arraycopy(protocArgs, 0, command, 1, protocArgs.length);
+
+ ProcessBuilder processBuilder = new ProcessBuilder(command);
+
+ processBuilder.redirectErrorStream(true);
+
+ Process process = processBuilder.start();
+
+ int exitCode = process.waitFor();
+
+ if (exitCode != 0) {
+ throw new IllegalStateException(
+ "Unable to generate file descriptor using system protoc. Exit code: " + exitCode);
+ }
+ } catch (IOException e) {
+ throw new UncheckedIOException(
+ "Unable to execute system protoc. Please ensure 'protoc' is installed and available in your system PATH.",
+ e);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ throw new IllegalStateException("Protoc execution was interrupted", e);
+ }
+ }
+
+ private FileDescriptorReader() {}
+}
diff --git a/impl/grpc/src/main/java/io/serverlessworkflow/impl/executors/grpc/GrpcChannelResolver.java b/impl/grpc/src/main/java/io/serverlessworkflow/impl/executors/grpc/GrpcChannelResolver.java
new file mode 100644
index 00000000..dabde19b
--- /dev/null
+++ b/impl/grpc/src/main/java/io/serverlessworkflow/impl/executors/grpc/GrpcChannelResolver.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2020-Present The Serverless Workflow Specification Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.serverlessworkflow.impl.executors.grpc;
+
+import io.grpc.Channel;
+import io.grpc.ManagedChannelBuilder;
+import io.serverlessworkflow.impl.TaskContext;
+import io.serverlessworkflow.impl.WorkflowContext;
+
+class GrpcChannelResolver {
+
+ static final String GRPC_CHANNEL_PROVIDER = "grpcChannelProvider";
+
+ static Channel channel(
+ WorkflowContext workflowContext,
+ TaskContext taskContext,
+ GrpcRequestContext grpcRequestContext) {
+ return workflowContext
+ .definition()
+ .application()
+ .additionalObject(GRPC_CHANNEL_PROVIDER, workflowContext, taskContext)
+ .orElseGet(
+ () ->
+ ManagedChannelBuilder.forAddress(
+ grpcRequestContext.address(), grpcRequestContext.port())
+ .usePlaintext()
+ .build());
+ }
+}
diff --git a/impl/grpc/src/main/java/io/serverlessworkflow/impl/executors/grpc/GrpcExecutor.java b/impl/grpc/src/main/java/io/serverlessworkflow/impl/executors/grpc/GrpcExecutor.java
new file mode 100644
index 00000000..44a10127
--- /dev/null
+++ b/impl/grpc/src/main/java/io/serverlessworkflow/impl/executors/grpc/GrpcExecutor.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright 2020-Present The Serverless Workflow Specification Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.serverlessworkflow.impl.executors.grpc;
+
+import com.google.protobuf.DescriptorProtos;
+import com.google.protobuf.Descriptors;
+import com.google.protobuf.DynamicMessage;
+import com.google.protobuf.InvalidProtocolBufferException;
+import com.google.protobuf.Message;
+import io.grpc.CallOptions;
+import io.grpc.Channel;
+import io.grpc.ClientCall;
+import io.grpc.MethodDescriptor;
+import io.grpc.protobuf.ProtoUtils;
+import io.grpc.stub.ClientCalls;
+import io.grpc.stub.StreamObserver;
+import io.serverlessworkflow.impl.TaskContext;
+import io.serverlessworkflow.impl.WorkflowContext;
+import io.serverlessworkflow.impl.WorkflowError;
+import io.serverlessworkflow.impl.WorkflowException;
+import io.serverlessworkflow.impl.WorkflowModel;
+import io.serverlessworkflow.impl.WorkflowValueResolver;
+import io.serverlessworkflow.impl.executors.CallableTask;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.CompletableFuture;
+
+public class GrpcExecutor implements CallableTask {
+
+ private final GrpcRequestContext requestContext;
+ private final WorkflowValueResolver