External Endpoints and Endpoint Catalogs
🚧 Documentation is under development
The Holoscan SDK guide is currently under active development. Some sections may be incomplete or change without notice.
Questions? Contact RidgeRun or email to support@ridgerun.com.
-
Overview
-
Build, Run, and Validate
-
Core Concepts
-
Operators and Graphs
-
Tensor Data and Memory
-
Interprocess Communication
-
-
Application and Operator Development
- Building an HSDK Application
- Writing a Minimal Operator
- Typed Parameters and Configuration
- Reading, Writing, and Forwarding Data
- Writing Tensor Operators
- Complete Operator Patterns
- Authoring Runtime Behavior
- Runtime Parameter Updates
- Lifecycle Callbacks and Restart Control
- Performance and Debugging
- SDK Source Map
-
Operator Reference
-
Core Operators
-
Holoscan Camera
-
Holoscan RidgeRun
- Overview
- Build and Installation
- ArgusCameraOp
- V4L2SourceOp
- V4L2UserPtrSourceOp
- BgraToPlanarTensorOp
- RawBayerToBgraOp
- RawBayerToYoloTensorOp
- YuyvToBgraOp
- TensorRtInferenceOp
- DrmDisplayOp
- DrmBgraDisplayOp
- X11DisplayOp
- CudaCompositorOp
- DepthColorizerOp
- FakesinkOp
- FrameSkipperOp
- InterprocessSrcOp
- TestPatternOp
-
-
Migration from Holoscan 4.x
-
Examples
-
Core
-
Holoscan Camera
-
Holoscan RidgeRun
-
-
Benchmark and Use Cases
-
Holoscan vs GStreamer
- Overview
-
Capture and Display
-
Capture and Inference
- Compare the workloads
- Implemented Applications
- Framework Selection
- Interprocess
-
External Endpoints and Endpoint Catalogs
An external endpoint gives a graph a named place where data enters or leaves the process. It is useful when a sensor service, recorder, user interface, or independently deployed pipeline needs its own lifecycle while still exchanging data with the graph.
The workflow is simple: choose the direction, describe the payload, set finite capacity and transfer rules, compile the graph, and give the accepted endpoint catalog to the program that will connect. The Channels and HoloMQ page explains the process-boundary choices, while this page shows how to declare and use one.
The Ports, Samples, and Payload Types page introduces the values that cross graph edges. The Tensor Contracts and Memory Types page defines the representation and storage facts that must also be accepted at an external boundary.
Start with the boundary decision
Use the following questions to choose the endpoint shape:
| Application need | Endpoint choice |
|---|---|
| A separate program supplies data to an operator | Import a topic into the graph. |
| A separate program receives data produced by an operator | Export a topic from the graph. |
| The message contains one Tensor | Add one finite Tensor boundary contract. |
| The message contains an image, IMU sample, or another schema with Tensor fields | Add one contract for every Tensor field and follow Descriptor-Tensor External Topics. |
| A separate application must attach to the deployed graph | Give it the accepted catalog and use the Standalone ChannelRuntime Clients workflow. |
An endpoint name identifies the contract that a client wants to use. It does not start the graph, create a live connection, or grant permission to change the graph. Keep the name stable when several deployment programs need to find the same endpoint.
Choose import or export direction
An import makes a graph input available to an external publisher. An export makes a graph output available to an external subscriber. The direction describes who supplies the value at the process boundary:
Graph::import_topic()creates a graph-side output that feeds an operator input.Graph::export_topic()creates a graph-side input connected to an operator output.
TopicOptions carries the endpoint name, attachment behavior, capture-time requirements, and direction-specific limits. Set the name and payload contract before importing or exporting the topic.
holoscan::TopicOptions input_options;
input_options.endpoint_name = "measurements-in";
input_options.attach = holoscan::TopicAttachPolicy{
.peer = holoscan::PeerRequirement::kOptional,
};
auto imported = graph.import_topic<holoscan::Tensor>(
"holomq://measurements/raw", input_options);
holoscan::TopicOptions output_options;
output_options.endpoint_name = "measurements-out";
output_options.attach = input_options.attach;
auto exported = graph.export_topic<holoscan::Tensor>(
"holomq://measurements/scaled", output_options);
The URI identifies the transport topic. The endpoint name is the catalog identity used by a standalone client. Both values are part of the deployment contract, so record them with the application configuration.
Declare a finite payload contract
The graph and the external program must agree on what one value can contain. For a raw Tensor, describe its memory kind, data type, rank, layout, and maximum storage span with TensorBoundaryContract. A Tensor type name alone does not describe the shape or storage that may arrive at the boundary.
The following fragment accepts a contiguous 64×3 host array. It is small enough to adapt to an application that already has a graph and a Tensor operator:
const holoscan::TensorBoundaryContract measurements{
.representation = holoscan::TensorRepresentation{
.memory_kind = holoscan::MemoryKind::kHost,
.dtype = DLDataType{kDLFloat, 32U, 1U},
.rank = 2U,
.strides = holoscan::TensorStridePolicy::kContiguousOnly,
},
.bounds = holoscan::tensor_bounds(64U * 3U * sizeof(float)),
};
input_options.tensor = measurements;
input_options.bounds.publisher = holoscan::PublisherEndpointBounds{
.max_loaned_samples = 2U,
};
output_options.tensor = measurements;
output_options.bounds.subscriber = holoscan::SubscriberEndpointBounds{
.queue_depth = 2U,
.max_borrowed_samples = 2U,
};
The contract admits the representation and maximum span. The application still checks the actual dimensions it receives. A byte limit alone does not prove that the value has the expected shape.
For a strided Tensor, declare finite extents and stride limits that cover the layout. For a descriptor-backed schema, declare a separate finite contract for every Tensor field. The Tensor Contracts and Memory Types page introduces these representation and storage decisions in more detail.
bounds.publisher applies to an external publisher feeding an import. bounds.subscriber applies to an external subscriber reading an export. Loan, queue, and borrow limits describe different resources, so provide the limit for the direction that the endpoint uses.
Set capacity and transfer policy
Capacity answers how much data and how many in-flight values the boundary can hold. Set it before deployment so a slow reader, retained sample, or burst does not turn into an unexpected allocation failure.
| Resource | Capacity decision |
|---|---|
| Tensor backing | Largest byte span for each Tensor, including strides, planes, and padding. |
| In-flight values | Number of published values that may wait while the graph or client processes them. |
| Imported or exported storage | Number of loans, borrows, or retained storage claims that may remain live. |
| Inline payload | Values that fit in the endpoint's inline limit and values that need a Tensor storage path. |
InterfaceTensorEmitPolicy tells the boundary what to do when the source storage is not directly usable:
| Policy | Use it when |
|---|---|
kRequireExportable
|
The application requires storage that is already exportable and layout-compatible. |
kCopyIfNeeded
|
A bounded staging copy is acceptable when direct sharing is unavailable. |
kInlineSmallHostThenCopyIfNeeded
|
Small host values may use an explicit inline cap before the admitted copy path is used. |
TensorTransferPolicy carries the transfer limits that accompany this decision. A copy-capable policy still needs enough bounded staging capacity, and a Tensor contract cannot create a missing device, transport capability, or exportable allocation. See Copy Paths and Memory Conversion for the application-side copy choices.
Compile and save the accepted catalog
After the graph connections and endpoint contracts are complete, compile the graph and inspect the accepted plan. ExecutionPlan::endpoint_catalog() exposes the endpoint description that clients must use.
const holoscan::ExecutionPlan plan = holoscan::compile(graph);
if (!plan.ok()) {
std::cerr << plan.json() << '\n';
return 1;
}
auto saved = plan.endpoint_catalog().save_json("endpoint-catalog.json");
if (!saved) {
return 1;
}
save_json() writes the accepted catalog to a file. Share that file with the client deployment. Saving it does not start the graph or confirm that a peer is currently running.
ChannelEndpointCatalog represents the immutable endpoint description. Its digest() identifies the normalized catalog content, which is useful for checking that graph and client deployments use the same contract.
Attach a client by catalog name
A standalone client loads the catalog and asks the runtime for the endpoint role it needs. The catalog-backed factories validate the endpoint identity, direction, payload shape, capacity, and required capabilities before the client starts its data loop.
auto catalog = holoscan::ChannelEndpointCatalog::load_json(
"endpoint-catalog.json");
auto runtime = holoscan::ChannelRuntime::create();
auto publisher = runtime->create_publisher<holoscan::Tensor>(
*catalog, "measurements-in");
auto subscriber = runtime->create_subscriber<holoscan::Tensor>(
*catalog, "measurements-out");
The catalog overloads of create_publisher<Tensor>() and create_subscriber<Tensor>() create the checked client endpoint. Continue with Standalone ChannelRuntime Clients for publishing, receiving, close, and error-handling code.
An optional peer may be absent when the client attaches. A required peer waits only within its configured bound. ChannelAttachOptions can set a shorter wait for the client operation, but it cannot change the endpoint payload or capacity contract.
Keep inline values and Tensor fields separate
Inline scalar and schema values use a bounded message area. The ordinary inline limit is 32,768 bytes, with catalog attribution and a schema envelope included in the frame. Large image and IMU Tensor fields need their own backing and retention contract; a descriptor does not make the Tensor bytes inline.
When a schema contains Tensor fields, use DescriptorTensorTopicContract<SchemaT> and cover every required field. Descriptor-Tensor External Topics shows the field-qualified declaration and matching client path.
When the endpoint carries event-time data, use RequireCaptureTime only when the producer supplies genuine capture coordinates in the admitted clock domain. The Sensor Payloads and Schemas page explains image and IMU timestamps and how their fields are interpreted.