Connector Release October 19, 2026 • 9 min read

Sub-Millisecond Event-Driven Ingestion: Announcing the gRPC Push Repository Connector

While traditional enterprise crawlers operate on a scheduled pull/poll paradigm—periodically scanning file systems, ECM repositories, or relational tables—modern event-driven architectures demand real-time, low-latency push ingestion. Change Data Capture (CDC) pipelines, enterprise webhook listeners, polyglot microservices in Go, Rust, or Python, and distributed crawler workers need to stream documents and lifecycle events directly into AI pipelines the millisecond they occur. Today, we are thrilled to announce the gRPC Push Repository Connector (oc-grpc-push-connector): a high-throughput HTTP/2 and Protocol Buffers v3 streaming ingestion gateway powered by Java 25 Virtual Threads, intelligent Claim-Check offloading, Zero-Trust ACL mapping, Mutual TLS (mTLS), W3C distributed trace propagation, and automated Lakehouse Parquet audit ledgers! ⚡📡

gRPC HTTP/2 Binary Push Ingestion & Decoupled RAG Architecture
Producers (CDC / Webhooks / Go / Python / CLI)
→
Netty gRPC Server (:9096 • Java 25 Virtual Threads)
→
Bearer / mTLS & W3C OTel Interceptor
→
OisDocumentTranslator & Claim-Check Offload
→
Kafka • Tika 4 • Vector Sinks
→
Lakehouse Parquet Ledger

1. Beyond Polling: The Case for Binary Push Ingestion

Earlier this year, we introduced OpenCrawling's gRPC Internal Transport Layer (oc-grpc-api) to accelerate inter-service communication between the crawler core and decoupled workers. Immediately, enterprise architects asked the natural follow-up question: "Can our external event producers push documents directly into OpenCrawling over gRPC with the same binary efficiency?"

In large-scale production environments, waiting for a 15-minute or hourly polling window is often too slow—and pushing multi-megabyte binary files encoded in Base64 over HTTP/1.1 JSON REST endpoints introduces severe serialization overhead:

2. Four Native gRPC Interaction Models (OisPushService)

Listening on default port 9096, the oc-grpc-push-connector exposes the strongly-typed opencrawling.push.v1.OisPushService contract, supporting four distinct RPC patterns tailored to every workload profile:

RPC Method Pattern Ideal Use Case & Behavior
PushDocument UNARY Single-document transactional webhooks and real-time event hooks. Returns an immediate PushDocumentResponse with status (ACCEPTED, QUEUED, or rejection code) and server timestamp.
StreamDocuments CLIENT STREAM High-throughput bulk ingestion streams over a single HTTP/2 connection. Upon stream completion, returns an aggregated StreamSummaryResponse detailing total_received, total_accepted, total_rejected, duration_ms, and granular PushFailure diagnostics.
BiDirectionalStream BIDI STREAM Full-duplex asynchronous streaming for continuous CDC pipelines and distributed crawlers. As the producer streams PushDocumentRequest frames, the server emits real-time per-document IngestionAck frames with thread-safe ordering and backpressure.
Ping DIAGNOSTIC Zero-overhead health and liveness check returning server status (UP), connector version, and epoch timestamp. Used by the Admin UI and CLI health probes.

The Canonical Protobuf v3 Contract (ois_push_service.proto)

The contract translates the Open Ingestion Standard (OIS) directly into Protocol Buffers v3:

syntax = "proto3";

package opencrawling.push.v1;

service OisPushService {
  rpc PushDocument (PushDocumentRequest) returns (PushDocumentResponse);
  rpc StreamDocuments (stream PushDocumentRequest) returns (StreamSummaryResponse);
  rpc BiDirectionalStream (stream PushDocumentRequest) returns (stream IngestionAck);
  rpc Ping (PingRequest) returns (PingResponse);
}

message PushDocumentRequest {
  string job_id = 1;                           // Target active OpenCrawling Job ID
  OisDocumentPayload document = 2;              // Document content, metadata, and ACLs
  PushOptions options = 3;                      // Pipeline routing and execution options
}

message OisDocumentPayload {
  string id = 1;                               // Unique document identifier (URI or primary key)
  DocumentAction action = 2;                   // UPSERT or DELETE
  string mime_type = 3;                        // MIME type (e.g. text/plain, application/pdf)
  int64 timestamp = 4;                         // Event timestamp (epoch milliseconds)

  oneof content_payload {
    bytes raw_bytes = 5;                       // Inline binary or raw text content
    string claim_check_uri = 6;                // Pre-uploaded claim check URI (file://, ozone://, s3://)
    string text_content = 7;                   // Direct plaintext content
  }

  map<string, string> metadata = 8;            // Dynamic structured metadata attributes
  SecurityConfig security = 9;                 // Document-level Zero-Trust security rules
  string checksum_sha256 = 10;                 // Optional SHA-256 for cryptographic integrity audit
}

message SecurityConfig {
  repeated string allow_sids = 1;              // Security Identifiers allowed to read (users/groups)
  repeated string deny_sids = 2;               // Security Identifiers denied access
  string owner = 3;                            // Document owner / creator SID
  string tenant_id = 4;                        // Multi-tenant isolation boundary
  map<string, string> attributes = 5;          // Custom ABAC attributes
}

3. Java 25 Virtual Threads & Intelligent Claim-Check Offloading

Under the hood, GrpcPushServerManager configures the shaded Netty HTTP/2 server to dispatch every incoming RPC onto a dedicated Java 25 Virtual Thread via Executors.newVirtualThreadPerTaskExecutor(). This allows thousands of concurrent streaming producers to push documents simultaneously without exhausting OS platform threads or blocking Netty event loops.

Even more importantly, the connector protects JVM heap memory and Kafka broker stability through three-tier payload handling in OisDocumentTranslator:

  1. Fast-Path In-Memory Streaming (≤ 512 KB): Inline raw_bytes or text_content payloads smaller than inline-binary-threshold-kb (default: 512 KB) flow through a lightweight ByteArrayInputStream directly into the orchestrator without redundant temporary disk writes.
  2. Automatic Claim-Check Offloading (> 512 KB): When an inline binary payload (such as a 25 MB PDF report or CAD drawing) exceeds inline-binary-threshold-kb, OisDocumentTranslator automatically streams the bytes into the active ClaimCheckStore (Local storage, Apache Ozone ofs:// / s3g, or AWS S3) and replaces the payload with a compact Claim-Check URI reference.
  3. Zero-Copy Pre-Uploaded claim_check_uri Pass-Through: If an upstream producer is handling multi-gigabyte archives or media files that already reside in Apache Ozone or S3, it can simply set claim_check_uri in the Protobuf oneof content_payload. OpenCrawling completely bypasses gRPC binary transfer and routes the URI directly to the decoupled Apache Tika 4 extraction consumers!
⚡

Adaptive Memory Protection: By combining configurable inbound frame limits (max-message-size-mb: 64) with automatic threshold-based Claim-Check offloading (inline-binary-threshold-kb: 512), Kafka messages remain tiny metadata envelopes regardless of document size.

4. Instant Tombstone DELETE Short-Circuiting

Keeping enterprise RAG vector stores synchronized with source-of-truth deletions is effortless. When a client pushes a request with action = DELETE:

5. Zero-Trust Security, Multi-Tenant ACLs & Mutual TLS (mTLS)

The gRPC Push Connector enforces defense-in-depth at both the transport layer and the document retrieval layer:

1. Transport Security: Constant-Time Bearer Auth & Mutual TLS

2. Document-Level Zero-Trust ACL Translation

OisDocumentTranslator.translateSecurity(...) maps the Protobuf SecurityConfig directly into OpenCrawling's unified org.opencrawling.core.security.SecurityConfig:

When AI agents query the vector index via the OpenCrawling Secure MCP Server, these permissions are strictly enforced at query time—ensuring zero unauthorized data leakage.

6. W3C Distributed Tracing & Lakehouse Audit Ledgers

Pushed documents enjoy the exact same enterprise observability and compliance governance as scheduled repository crawls:

🦆

Stream Session Aggregation: During a StreamDocuments or BiDirectionalStream session, all 10,000+ pushed documents are grouped under a single exec-stream-* execution ID, producing one consolidated Parquet audit ledger and updating live job throughput counters in real time.

7. Developer Experience: oc push CLI, grpcurl & Java 25 Client

We built first-class tooling into the OpenCrawling CLI (oc-cli), the embedded gRPC reflection service, and the Admin Dashboard so you can test and operate gRPC push pipelines in seconds.

1. Native OpenCrawling CLI (oc push)

# 1. Probe gRPC Push listener health and round-trip latency
$ oc push ping --host 127.0.0.1 --port 9096

# 2. Verify via Admin REST API Connector Checker
$ oc connector check --name Grpc_Push_Connector --type repository --config host=127.0.0.1 --config port=9096

# 3. Push an inline UPSERT document with Zero-Trust ACLs
$ oc push upsert \
  --job-id 1 \
  --id "https://ecm.example.com/contracts/2026-msa.md" \
  --text "# Master Services Agreement 2026..." \
  --acl-allow "GROUP_LEGAL,ROLE_EXECUTIVE" \
  --acl-deny "GROUP_CONTRACTORS" \
  --metadata "department=legal" \
  --token "my-secret-token"

# 4. Stream an entire directory of files over Bi-Directional HTTP/2 gRPC
$ oc push stream --bidi --job-id 1 --dir ./docs/contracts --token "my-secret-token"

# 5. Push a DELETE tombstone to purge vectors
$ oc push delete --job-id 1 --id "https://ecm.example.com/contracts/2026-msa.md" --token "my-secret-token"

2. Interactive Exploration with grpcurl (Server Reflection Enabled)

Because ProtoReflectionService is enabled by default, tools like grpcurl, Postman, and Kreya auto-discover the schema without needing local .proto files:

# Discover exposed gRPC services
$ grpcurl -plaintext 127.0.0.1:9096 list

# Push a document with Zero-Trust ACLs via grpcurl
$ grpcurl -plaintext \
  -H "authorization: Bearer my-secret-token" \
  -d '{
    "job_id": "1",
    "document": {
      "id": "https://ecm.example.com/contracts/2026-msa.md",
      "action": "UPSERT",
      "mime_type": "text/markdown",
      "text_content": "# Master Services Agreement\n\nGoverns enterprise cloud services...",
      "metadata": {
        "department": "legal",
        "contract_year": "2026"
      },
      "security": {
        "allow_sids": ["GROUP_LEGAL", "ROLE_EXECUTIVE"],
        "deny_sids": ["GROUP_CONTRACTORS"],
        "owner": "alice.smith",
        "tenant_id": "acme-corp"
      }
    },
    "options": {
      "immediate_flush": true,
      "routing_key": "legal-contracts"
    }
  }' \
  127.0.0.1:9096 opencrawling.push.v1.OisPushService/PushDocument

3. Embedding a Java 25 Bi-Directional Streaming Producer

import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.grpc.Metadata;
import io.grpc.stub.MetadataUtils;
import io.grpc.stub.StreamObserver;
import org.opencrawling.push.v1.*;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

public class GrpcBiDiStreamingProducer {
    public static void main(String[] args) throws InterruptedException {
        ManagedChannel channel = ManagedChannelBuilder.forAddress("127.0.0.1", 9096)
                .usePlaintext()
                .build();

        Metadata headers = new Metadata();
        headers.put(Metadata.Key.of("authorization", Metadata.ASCII_STRING_MARSHALLER),
                "Bearer my-secret-token");

        OisPushServiceGrpc.OisPushServiceStub asyncStub = OisPushServiceGrpc.newStub(channel)
                .withInterceptors(MetadataUtils.newAttachHeadersInterceptor(headers));

        CountDownLatch done = new CountDownLatch(1);
        StreamObserver<PushDocumentRequest> requestObserver = asyncStub.biDirectionalStream(
                new StreamObserver<>() {
                    @Override
                    public void onNext(IngestionAck ack) {
                        System.out.printf("ACK doc=%s status=%s at=%d%n",
                                ack.getDocumentId(), ack.getStatus(), ack.getAckTimestamp());
                    }

                    @Override
                    public void onError(Throwable t) {
                        t.printStackTrace();
                        done.countDown();
                    }

                    @Override
                    public void onCompleted() {
                        done.countDown();
                    }
                });

        for (int i = 1; i <= 5; i++) {
            requestObserver.onNext(PushDocumentRequest.newBuilder()
                    .setJobId("1")
                    .setDocument(OisDocumentPayload.newBuilder()
                            .setId("doc://kb/article-" + i)
                            .setAction(DocumentAction.UPSERT)
                            .setMimeType("text/plain")
                            .setTextContent("Real-time event-driven knowledge base entry #" + i)
                            .setSecurity(SecurityConfig.newBuilder()
                                    .addAllowSids("GROUP_ENGINEERING")
                                    .setTenantId("acme-corp")
                                    .build())
                            .build())
                    .build());
        }
        requestObserver.onCompleted();
        done.await(10, TimeUnit.SECONDS);
        channel.shutdownNow();
    }
}

8. Configuration Reference

Configure the gRPC Push Repository Connector via application.yml, environment variables, or the Admin UI Connector Registry (which includes a live Test gRPC Push Listener (Ping) diagnostic button):

Environment Variable Spring Boot Property Default Description
OPENCRAWLING_GRPC_PUSH_ENABLED opencrawling.connector.grpc-push.enabled true Enable or disable the embedded Netty gRPC Push server
OPENCRAWLING_GRPC_PUSH_PORT opencrawling.connector.grpc-push.port 9096 Inbound TCP port for the gRPC Push listener
OPENCRAWLING_GRPC_PUSH_MAX_MESSAGE_SIZE_MB opencrawling.connector.grpc-push.max-message-size-mb 64 Maximum inbound gRPC message frame size in MB
OPENCRAWLING_GRPC_PUSH_INLINE_THRESHOLD_KB opencrawling.connector.grpc-push.inline-binary-threshold-kb 512 Payload threshold in KB before automatic Claim-Check offload
OPENCRAWLING_GRPC_PUSH_TOKEN opencrawling.connector.grpc-push.token "" Optional Bearer token required in authorization metadata
OPENCRAWLING_GRPC_PUSH_REFLECTION_ENABLED opencrawling.connector.grpc-push.reflection.enabled true Enable gRPC Server Reflection for grpcurl & Postman
OPENCRAWLING_GRPC_PUSH_TLS_ENABLED opencrawling.connector.grpc-push.tls.enabled false Enable TLS / mTLS encryption on the gRPC listener
OPENCRAWLING_GRPC_PUSH_TLS_CERT opencrawling.connector.grpc-push.tls.cert-chain-path "" Path to server X.509 certificate chain file (PEM)
OPENCRAWLING_GRPC_PUSH_TLS_KEY opencrawling.connector.grpc-push.tls.private-key-path "" Path to server PKCS#8 private key file (PEM)
OPENCRAWLING_GRPC_PUSH_TLS_TRUST_CERT opencrawling.connector.grpc-push.tls.trust-cert-path "" Path to trusted CA certificates for mTLS verification
OPENCRAWLING_GRPC_PUSH_TLS_CLIENT_AUTH opencrawling.connector.grpc-push.tls.client-auth NONE mTLS policy: NONE, OPTIONAL, or REQUIRE

9. Automated Verification & Decoupled Pipeline Testing

The gRPC Push Repository Connector is verified across both in-process unit/integration test suites and the full containerized decoupled stack:

✅

Unit & In-Process gRPC Suite: Run ./scripts/test-grpc-push-connector.sh to execute GrpcPushConnectorTest, OisPushServiceInProcessTest, and RuntimePushDocumentDispatcherTest—verifying Unary, Client-Streaming, and Bi-Directional RPCs, 512 KB Claim-Check offloading thresholds, Zero-Trust ACL translation, Bearer auth, W3C traceparent propagation, and synchronous Parquet/CSV/JSON report generation.

✅

Full Decoupled Docker Pipeline Test: Run ./scripts/test-grpc-push-decoupled.sh (or ./run-integration-tests.sh grpc-push) to spin up the complete multi-container stack, verify Ping and /api/connectors/check, assert UNAUTHENTICATED rejection without a Bearer token, push an authenticated UPSERT over port 9096 through Kafka + Claim-Check + Ollama + pgvector, verify the exec-push-* audit ledger in /api/reports, and confirm vector eviction via a gRPC DELETE tombstone.

Ready to Stream Real-Time Events into Enterprise RAG?

Explore the OpenCrawling architecture in our live interactive simulator, read the complete gRPC Push Connector documentation on our Wiki, or star the repository on GitHub.