Choose a protocol

Select between gRPC and REST for your Lattice SDK integration

The Lattice SDK supports both REST and gRPC protocols. REST offers familiar HTTP/JSON workflows with automatic authentication and broad tooling support.

RESTgRPC
InstallationPackages via PyPi, Maven, npm, source via GitHubPackages generated in Buf Schema Registry
Language SupportPython, Typescript, Java, and GoAuto-generated bindings for 16 languages
AuthenticationIncludes built-in support for OAuth 2.0Requires manual token lifecycle management
StreamingSupported with basic filteringSupported with advanced filtering
API availabilityEntities, Tasks, and ObjectsEntities and Tasks
RetriesBuilt-in retries for 5xx, 408, 409, and 429 responsesNo built-in retries, implement them in your integration
On-the-wire sizeLarger packets with JSON encodingSmaller packets with binary encoding
Browser supportBetter browser supportWeaker browser support

gRPC provides binary Protocol Buffers encoding that reduces bandwidth in constrained networks. For high-frequency scenarios, the payload difference compounds quickly. Publishing 50 drone positions at 10 Hz requires ~2 Mbps with REST and JSON, compared to ~1.3 Mbps with gRPC and Protobuf:

500 bytes × 10 updates/sec × 50 drones = 250 KB/sec = ~2 Mbps
gRPC authentication

If you are using gRPC with client credentials, set up the token refresh module before running the examples on this page.

Authentication

If you are using client credentials, your authentication set up differs between REST and gRPC. REST SDKs automatically manage OAuth token lifecycle, while gRPC requires manual token management with custom credential providers.

For complete authentication details including OAuth client credentials, environment tokens, and code examples for both protocols, see the Authenticate guide.

Reliability and retries

Networks that carry Lattice traffic, such as tactical links, satellite relays, and congested cloud egress, drop connections, throttle traffic, and return transient errors. How much of that your integration has to handle manually depends on the protocol you choose.

REST: Built-in retries for transient errors

The Lattice REST SDKs retry automatically on HTTP 5xx, 408, 409, and 429 responses, using exponential backoff starting at 1 second with a maximum of 60 seconds and ±20% jitter. The SDK also honors Retry-After, retry-after-ms, and X-RateLimit-Reset headers returned by the server. The default is 2 retries per call, and you can override this per call with request_options={"max_retries": N}.

For REST integrations, the baseline reliability story is already in the SDK. You only need a custom retry utility if you want to retry additional error classes (for example, 404 under eventual consistency) or centralize backoff policy across every call site.

gRPC: No built-in retries

The Lattice gRPC SDKs do not retry anything automatically. Every call that returns a non-OK status surfaces the error to your code unchanged. For gRPC integrations, you must implement retry logic yourself: classify gRPC status codes as retryable or terminal, apply an exponential backoff, and honor context cancellation.

For a modular, utility-driven pattern that works for both protocols, see Retry connections.

Managing objects

The Objects API is a REST-only content delivery network (CDN) service for uploading and managing files and binary data in Lattice. This API is not available in gRPC. If your integration requires file operations alongside gRPC entity or task operations, you need to use both protocols.

For more information on managing files and binary data, see Objects overview.

Publishing entities

The choice between REST and gRPC for publishing entities depends on your update frequency and data volume.

gRPC: High-throughput streaming

Use the PublishEntities API when your integration produces high volumes of entity updates. This API creates a client-side stream that efficiently publishes batches of entities in a single connection. This approach is ideal for integrations that generate large quantities of track detections, such as passive sensors or radar systems tracking multiple targets simultaneously.

1// This Go example is compatible with artifacts generated using
2// the following grpc/go plugin: https://buf.build/anduril/lattice-sdk/sdks/main:grpc/go
3package main
4
5import (
6 "context"
7 "fmt"
8 "log"
9 "os"
10 "time"
11
12 "buf.build/gen/go/anduril/lattice-sdk/grpc/go/anduril/entitymanager/v1/entitymanagerv1grpc"
13 entitymanagerv1 "buf.build/gen/go/anduril/lattice-sdk/protocolbuffers/go/anduril/entitymanager/v1"
14 ontologyv1 "buf.build/gen/go/anduril/lattice-sdk/protocolbuffers/go/anduril/ontology/v1"
15 "github.com/google/uuid"
16 "google.golang.org/grpc"
17 "google.golang.org/grpc/credentials"
18 "google.golang.org/protobuf/types/known/timestamppb"
19 "google.golang.org/protobuf/types/known/wrapperspb"
20)
21
22func main() {
23 ctx := context.Background()
24
25 // Get environment variables
26 clientID := os.Getenv("LATTICE_CLIENT_ID")
27 clientSecret := os.Getenv("LATTICE_CLIENT_SECRET")
28 latticeEndpoint := os.Getenv("LATTICE_ENDPOINT")
29 sandboxesToken := os.Getenv("SANDBOXES_TOKEN")
30
31 if latticeEndpoint == "" || clientSecret == "" || clientID == "" || sandboxesToken == "" {
32 log.Fatalf("Missing required environment variables")
33 }
34
35 // Set up authentication
36 authCredentials := &ClientCredentialsAuth{
37 ClientID: clientID,
38 ClientSecret: clientSecret,
39 SandboxesToken: sandboxesToken,
40 Endpoint: fmt.Sprintf("https://%s/api/v1/oauth/token", latticeEndpoint),
41 }
42
43 // Create gRPC connection
44 opts := []grpc.DialOption{
45 grpc.WithTransportCredentials(credentials.NewClientTLSFromCert(nil, "")),
46 grpc.WithPerRPCCredentials(authCredentials),
47 }
48 conn, err := grpc.NewClient(latticeEndpoint, opts...)
49 if err != nil {
50 log.Fatalf("Failed to connect: %v", err)
51 }
52 defer conn.Close()
53
54 client := entitymanagerv1grpc.NewEntityManagerAPIClient(conn)
55
56 // Create client-side streaming connection
57 stream, err := client.PublishEntities(ctx)
58 if err != nil {
59 log.Fatalf("Failed to create stream: %v", err)
60 }
61
62 log.Println("Publishing entities via client-side streaming...")
63
64 // Send multiple entities through the stream
65 for i := range 100 {
66 entity := &entitymanagerv1.Entity{
67 EntityId: fmt.Sprintf("entity-%s", uuid.New().String()[:8]),
68 Description: "Streaming track example",
69 Aliases: &entitymanagerv1.Aliases{
70 Name: fmt.Sprintf("Track-%d", i),
71 },
72 IsLive: true,
73 CreatedTime: timestamppb.Now(),
74 ExpiryTime: timestamppb.New(time.Now().Add(10 * time.Second)),
75 Ontology: &entitymanagerv1.Ontology{
76 Template: entitymanagerv1.Template_TEMPLATE_TRACK,
77 PlatformType: "UNKNOWN",
78 },
79 MilView: &entitymanagerv1.MilView{
80 Disposition: ontologyv1.Disposition_DISPOSITION_UNKNOWN,
81 Environment: ontologyv1.Environment_ENVIRONMENT_UNKNOWN,
82 },
83 Location: &entitymanagerv1.Location{
84 Position: &entitymanagerv1.Position{
85 LatitudeDegrees: 33.6405 + float64(i)*0.001,
86 LongitudeDegrees: -117.6738 + float64(i)*0.001,
87 AltitudeHaeMeters: wrapperspb.Double(100.0),
88 },
89 },
90 Provenance: &entitymanagerv1.Provenance{
91 IntegrationName: "streaming_example",
92 DataType: "track_data",
93 SourceUpdateTime: timestamppb.Now(),
94 },
95 }
96
97 // Send entity through stream
98 if err := stream.Send(&entitymanagerv1.PublishEntitiesRequest{Entity: entity}); err != nil {
99 log.Fatalf("Failed to send entity: %v", err)
100 }
101
102 if (i+1)%10 == 0 {
103 log.Printf("Sent %d entities", i+1)
104 }
105
106 // Throttle the call
107 time.Sleep(300 * time.Millisecond)
108 }
109
110 // Close the stream and receive response
111 _, err = stream.CloseAndRecv()
112 if err != nil {
113 log.Fatalf("Failed to close stream: %v", err)
114 }
115
116 log.Println("Successfully published entities")
117}

REST: Individual entity updates

Use the PublishEntity API when your integration publishes entities individually or at lower frequencies. This unary API publishes one entity per request, making it well-suited for stateful entities like assets that update their position and status periodically rather than continuously.

Go
1package main
2
3import (
4 "context"
5 "fmt"
6 "math"
7 "net/http"
8 "os"
9 "time"
10
11 Lattice "github.com/anduril/lattice-sdk-go/v4"
12 "github.com/anduril/lattice-sdk-go/v4/client"
13 "github.com/anduril/lattice-sdk-go/v4/option"
14 "github.com/google/uuid"
15)
16
17// velocityToYawEnu converts an ENU velocity vector into a yaw angle, in
18// radians. It uses the ENU mathematical convention where a yaw of 0 points
19// East and a yaw of pi/2 points North.
20func velocityToYawEnu(velocityEast, velocityNorth float64) float64 {
21 return math.Atan2(velocityNorth, velocityEast)
22}
23
24// yawToQuaternionEnu converts a yaw angle into the attitude_enu quaternion.
25// Yaw is a rotation about the ENU Up (Z) axis, so the quaternion only has Z
26// and W components. The result is already unit-normalized.
27func yawToQuaternionEnu(yawRad float64) *Lattice.Quaternion {
28 return &Lattice.Quaternion{
29 X: Lattice.Float64(0.0),
30 Y: Lattice.Float64(0.0),
31 Z: Lattice.Float64(math.Sin(yawRad / 2.0)),
32 W: Lattice.Float64(math.Cos(yawRad / 2.0)),
33 }
34}
35
36func main() {
37 // Get environment variables
38 latticeEndpoint := os.Getenv("LATTICE_ENDPOINT")
39 clientSecret := os.Getenv("LATTICE_CLIENT_SECRET")
40 clientId := os.Getenv("LATTICE_CLIENT_ID")
41
42 // Remove sandboxesToken from the following statements if you are not developing on Sandboxes
43 sandboxesToken := os.Getenv("SANDBOXES_TOKEN")
44
45 // Check required environment variables
46 if latticeEndpoint == "" || clientId == "" || clientSecret == "" || sandboxesToken == "" {
47 fmt.Println("Missing required environment variables")
48 os.Exit(1)
49 }
50
51 // Initialize headers for sandbox authorization
52 headers := http.Header{}
53 headers.Add("Anduril-Sandbox-Authorization", fmt.Sprintf("Bearer %s", sandboxesToken))
54
55 // Create the client
56 LatticeClient := client.NewClient(
57 option.WithClientCredentials(clientId, clientSecret),
58 option.WithBaseURL(fmt.Sprintf("https://%s", latticeEndpoint)),
59 option.WithHTTPHeader(headers),
60 )
61
62 // Generate a unique ID for the entity
63 entityId := uuid.New().String()
64
65 // Set a radius, in degrees, to simulate the entity moving in a circle
66 radiusDegrees := 0.1
67 count := 0.0
68 centerLat := 50.91402185768586
69 centerLon := 0.79203612077257
70 creationTime := time.Now().UTC()
71
72 // Continuously publish the entity
73 for {
74 latestTimestamp := time.Now().UTC()
75 ctx := context.Background()
76
77 // Update position
78 count += 0.1
79 t := math.Pi * count / 180.0
80
81 // Derive the ENU velocity from the circular motion, then convert it
82 // into a yaw angle and an attitude_enu quaternion.
83 lat := centerLat + (radiusDegrees * math.Cos(t))
84 metersPerDegreeLat := 111320.0
85 metersPerDegreeLon := 111320.0 * math.Cos(lat*math.Pi/180.0)
86 velocityEast := radiusDegrees * math.Cos(t) * metersPerDegreeLon
87 velocityNorth := -radiusDegrees * math.Sin(t) * metersPerDegreeLat
88 yawRad := velocityToYawEnu(velocityEast, velocityNorth)
89
90 // Create entity to publish
91 entity := Lattice.Entity{
92 EntityID: &entityId,
93 Description: Lattice.String("Friendly drone asset"),
94 Aliases: &Lattice.Aliases{
95 Name: Lattice.String("Drone 1"),
96 },
97 IsLive: Lattice.Bool(true),
98 CreatedTime: Lattice.Time(creationTime),
99 ExpiryTime: Lattice.Time(latestTimestamp.Add(1 * time.Hour)),
100 Ontology: &Lattice.Ontology{
101 Template: Lattice.OntologyTemplateTemplateAsset.Ptr(),
102 PlatformType: Lattice.String("UAV"),
103 },
104 MilView: &Lattice.MilView{
105 Disposition: Lattice.MilViewDispositionDispositionFriendly.Ptr(),
106 Environment: Lattice.MilViewEnvironmentEnvironmentAir.Ptr(),
107 },
108 Location: &Lattice.Location{
109 Position: &Lattice.Position{
110 LatitudeDegrees: Lattice.Float64(centerLat + (radiusDegrees * math.Cos(t))),
111 LongitudeDegrees: Lattice.Float64(centerLon + (radiusDegrees * math.Sin(t))),
112 AltitudeAsfMeters: Lattice.Float64(1000),
113 },
114 // Report the velocity in the ENU frame, in meters per second.
115 // Vertical velocity is 0 at constant altitude.
116 VelocityEnu: &Lattice.Enu{
117 E: Lattice.Float64(velocityEast),
118 N: Lattice.Float64(velocityNorth),
119 U: Lattice.Float64(0.0),
120 },
121 // Report the heading as a quaternion derived from velocity.
122 AttitudeEnu: yawToQuaternionEnu(yawRad),
123 },
124 Provenance: &Lattice.Provenance{
125 IntegrationName: Lattice.String("your_integration_name"),
126 DataType: Lattice.String("your_data_type"),
127 SourceUpdateTime: Lattice.Time(latestTimestamp),
128 },
129 Health: &Lattice.Health{
130 ConnectionStatus: Lattice.HealthConnectionStatusConnectionStatusOnline.Ptr(),
131 HealthStatus: Lattice.HealthHealthStatusHealthStatusHealthy.Ptr(),
132 // Report the health of individual subsystems in components.
133 // Each component needs a stable ID, a display name, and a
134 // health status. Here, the drone reports its battery as healthy.
135 Components: []*Lattice.ComponentHealth{
136 {
137 ID: Lattice.String("battery-0"),
138 Name: Lattice.String("Battery"),
139 Health: Lattice.ComponentHealthHealthHealthStatusHealthy.Ptr(),
140 Messages: []*Lattice.ComponentMessage{
141 {
142 Status: Lattice.ComponentMessageStatusHealthStatusHealthy.Ptr(),
143 Message: Lattice.String("Battery at 87% charge."),
144 },
145 },
146 UpdateTime: Lattice.Time(latestTimestamp),
147 },
148 },
149 // Live list of active alerts for the asset. Remove entries
150 // when the underlying condition clears; Lattice does not
151 // filter stale entries automatically.
152 ActiveAlerts: []*Lattice.Alert{},
153 UpdateTime: Lattice.Time(latestTimestamp),
154 },
155 // Declare the sensors carried by the asset. Each entry requires a
156 // stable sensor_id, a sensor_type, and at least one field of view
157 // so Lattice can render the sensor cone.
158 Sensors: &Lattice.Sensors{
159 Sensors: []*Lattice.Sensor{
160 {
161 SensorID: Lattice.String("camera-1"),
162 SensorType: Lattice.SensorSensorTypeSensorTypeCamera.Ptr(),
163 OperationalState: Lattice.SensorOperationalStateOperationalStateOperational.Ptr(),
164 FieldsOfView: []*Lattice.FieldOfView{
165 {
166 CenterRayPose: &Lattice.EntityManagerPose{
167 Orientation: &Lattice.Quaternion{
168 X: Lattice.Float64(0.0),
169 Y: Lattice.Float64(0.0),
170 Z: Lattice.Float64(0.0),
171 W: Lattice.Float64(1.0),
172 },
173 },
174 HorizontalFov: Lattice.Float64(1.047),
175 VerticalFov: Lattice.Float64(0.785),
176 Range: Lattice.Float64(2000.0),
177 Mode: Lattice.FieldOfViewModeSensorModeSearch.Ptr(),
178 },
179 },
180 },
181 },
182 },
183 TaskCatalog: &Lattice.TaskCatalog{
184 TaskDefinitions: []*Lattice.TaskDefinition{
185 {TaskSpecificationURL: Lattice.String("type.googleapis.com/anduril.tasks.v2.VisualId")},
186 {TaskSpecificationURL: Lattice.String("type.googleapis.com/anduril.tasks.v2.Investigate")},
187 },
188 },
189 }
190
191 // Publish the entity
192 _, err := LatticeClient.Entities.PublishEntity(ctx, &entity)
193
194 // Handle errors
195 if err != nil {
196 fmt.Printf("Error publishing entity: %v\n", err)
197 } else {
198 fmt.Println("Publishing asset")
199 }
200
201 // Wait before next request
202 time.Sleep(1 * time.Second)
203 }
204}

For detailed guidance on entity publishing patterns and best practices, see Publish entities.

Streaming entities

Both protocols support server-side streaming for consuming entity updates, with different filtering capabilities.

gRPC: Advanced filtering

Use StreamEntityComponents when you need complex filtering logic to receive only relevant entities. This API offers more robust filtering capabilities, letting you specify filter criteria for more fine-tuned streaming.

1// This Go example is compatible with artifacts generated using
2// the following grpc/go plugin: https://buf.build/anduril/lattice-sdk/sdks/main:grpc/go
3package main
4
5import (
6 "context"
7 "fmt"
8 "io"
9 "log"
10 "os"
11
12 "buf.build/gen/go/anduril/lattice-sdk/grpc/go/anduril/entitymanager/v1/entitymanagerv1grpc"
13 entitymanagerv1 "buf.build/gen/go/anduril/lattice-sdk/protocolbuffers/go/anduril/entitymanager/v1"
14 "google.golang.org/grpc"
15 "google.golang.org/grpc/credentials"
16)
17
18func main() {
19 ctx := context.Background()
20
21 clientID := os.Getenv("LATTICE_CLIENT_ID")
22 clientSecret := os.Getenv("LATTICE_CLIENT_SECRET")
23 latticeEndpoint := os.Getenv("LATTICE_ENDPOINT")
24 sandboxesToken := os.Getenv("SANDBOXES_TOKEN")
25
26 if latticeEndpoint == "" || clientSecret == "" || clientID == "" || sandboxesToken == "" {
27 log.Fatalf("Missing required environment variables")
28 }
29 auth := &ClientCredentialsAuth{
30 ClientID: clientID,
31 ClientSecret: clientSecret,
32 SandboxesToken: sandboxesToken,
33 Endpoint: fmt.Sprintf("https://%s/api/v1/oauth/token", latticeEndpoint),
34 }
35
36 opts := []grpc.DialOption{
37 grpc.WithTransportCredentials(credentials.NewClientTLSFromCert(nil, "")),
38 grpc.WithPerRPCCredentials(auth),
39 }
40 conn, err := grpc.NewClient(latticeEndpoint, opts...)
41
42 if err != nil {
43 log.Fatalf("Did not connect: %v", err)
44 }
45 defer conn.Close()
46
47 client := entitymanagerv1grpc.NewEntityManagerAPIClient(conn)
48
49 stream, err := client.StreamEntityComponents(ctx, &entitymanagerv1.StreamEntityComponentsRequest{
50 ComponentsToInclude: []string{"aliases", "location"},
51 })
52 if err != nil {
53 log.Fatalf("Error creating stream: %v", err)
54 }
55
56 log.Println("Starting to receive stream data...")
57 for {
58 response, err := stream.Recv()
59 if err == io.EOF {
60 // End of stream
61 log.Println("End of stream reached.")
62 break
63 }
64 if err != nil {
65 log.Fatalf("Error receiving stream data: %v", err)
66 }
67
68 entity := response.GetEntityEvent().GetEntity()
69 if position := entity.GetLocation().GetPosition(); position != nil {
70 log.Printf("Entity %s at location: %f, %f",
71 entity.EntityId,
72 position.LatitudeDegrees,
73 position.LongitudeDegrees,
74 )
75 }
76 }
77}

REST: Component-based filtering

Use StreamEntities for server-side streaming over HTTP with component-based filtering. This API filters entities based on the presence of specific components, which is sufficient for most integration scenarios where you need entities with particular data.

Go
1package main
2
3import (
4 "context"
5 "errors"
6 "fmt"
7 "io"
8 "log"
9 "net/http"
10 "os"
11
12 Lattice "github.com/anduril/lattice-sdk-go/v4"
13 "github.com/anduril/lattice-sdk-go/v4/client"
14 "github.com/anduril/lattice-sdk-go/v4/option"
15)
16
17func main() {
18 ctx, cancel := context.WithCancel(context.Background())
19 defer cancel()
20
21 latticeEndpoint := os.Getenv("LATTICE_ENDPOINT")
22 clientSecret := os.Getenv("LATTICE_CLIENT_SECRET")
23 clientId := os.Getenv("LATTICE_CLIENT_ID")
24 sandboxesToken := os.Getenv("SANDBOXES_TOKEN")
25
26 if latticeEndpoint == "" || clientId == "" || clientSecret == "" || sandboxesToken == "" {
27 log.Fatal("Required environment variables not set.")
28 }
29
30 headers := http.Header{}
31 headers.Add("Anduril-Sandbox-Authorization", fmt.Sprintf("Bearer %s", sandboxesToken))
32
33 latticeClient := client.NewClient(
34 option.WithClientCredentials(clientId, clientSecret),
35 option.WithBaseURL(fmt.Sprintf("https://%s", latticeEndpoint)),
36 option.WithHTTPHeader(headers),
37 )
38
39 // Create the entity stream.
40 stream, err := latticeClient.Entities.StreamEntities(ctx, &Lattice.EntityStreamRequest{
41 PreExistingOnly: Lattice.Bool(false),
42 // Define a list of components to control which entities are fetched.
43 // If set, Lattice streams only entities with the components you provide.
44 ComponentsToInclude: []string{"aliases", "location_uncertainty"},
45 })
46 if err != nil {
47 log.Fatalf("Failed to create entity stream: %v", err)
48 }
49 defer stream.Close()
50
51 for {
52 select {
53 case <-ctx.Done():
54 log.Printf("Context canceled: %v", ctx.Err())
55 return
56 default:
57 // Continue processing
58 }
59 message, err := stream.Recv()
60
61 // Handle stream completion
62 if errors.Is(err, io.EOF) {
63 log.Println("Stream completed successfully.")
64 return
65 }
66
67 if err != nil {
68 log.Printf("Error receiving message: %v", err)
69 continue
70 }
71
72 // Process the event based whether it is a heartbeat or entity event.
73 switch message.Event {
74 case "heartbeat":
75 timestamp := *message.Heartbeat.Timestamp
76 log.Printf("Heartbeat: %s", timestamp)
77 case "entity":
78 log.Printf("Entity: %s", *message.Entity.Entity.Aliases.Name)
79 default:
80 log.Printf("Unknown event type: %s", message.Event)
81 }
82 }
83}

For more information on streaming patterns and filtering strategies, see Watch entities.

Decision guide

Use this three-step framework to guide your protocol choice:

1

When to use gRPC

Choose gRPC when your integration prioritizes performance and bandwidth efficiency:

  • Bandwidth-constrained networks: Tactical networks, satellite links, or environments where data transmission costs matter use gRPC.
  • High-throughput scenarios: Integrations that publishing hundreds of entity updates rapidly use gRPC.
  • Specialized languages: Integrations written in Rust, C++, or other languages beyond the REST SDK use gRPC bindings.
  • Custom reliability logic: Integrations that need more control over retry classification, backoff, and error handling implement it directly in their integration, since the gRPC SDKs ship no retry layer of their own.
Scenario

An autonomous drone fleet publishes sensor data and location updates at 10 Hz per vehicle. With 50 drones, you’re sending 500 entity updates per second. gRPC’s binary encoding reduces each payload by 30-50% compared to JSON, significantly decreasing bandwidth requirements.

2

When to use REST

Choose REST when your integration prioritizes development velocity and ecosystem compatibility:

  • Web applications: Dashboards, command and control interfaces, or browser-based tools use REST
  • Low-frequency operations: Periodic polling, manual workflows, or asynchronous task creation use REST
  • Objects API access: Services that manage files and binary data in Lattice use REST to integrate with the Objects API.
  • Built-in reliability: Integrations that want sensible retry defaults for transient server errors without writing a retry layer benefit from the REST SDK’s automatic retries on 5xx, 408, 409, and 429 responses.
Scenario

A command and control web dashboard queries Lattice every 5 seconds to display current entity positions. The interface creates tasks when operators interact with the UI. REST’s simplicity accelerates development, and browser compatibility is essential.

3

When to use both

Many production systems combine both protocols to leverage their respective strengths:

  • Fleet management platforms: Hardware components use gRPC for telemetry, while web interfaces use REST for visualization.
  • Multi-component systems: Edge devices publish updates using gRPC, and cloud services consume the data using REST.
  • Objects integration: Core entity, and tasking operations use gRPC, and file management is implemented in REST.
Scenario

A robotics platform has autonomous ground vehicles publishing position and sensor data via gRPC at 5 Hz. A React web application consumes this data via REST polling at 1 Hz for operator display. Mission plans are uploaded as files through the REST Objects API, then referenced in tasks sent to the robots.

What’s next