Skip to main content

Remote Access Gateway

The Foxglove SDK can run a remote access gateway on a device, making it available for remote visualization and teleoperation through the Foxglove platform. The gateway connects to Foxglove so that organization members can access the device from anywhere, without direct network access. This is useful when the device is behind a firewall, on a cellular network, or otherwise unreachable from your local machine.

Starting the gateway

The gateway authenticates with a device token. You can provide the token directly or set the FOXGLOVE_DEVICE_TOKEN environment variable.

use foxglove::remote_access::Gateway;

let handle = Gateway::new()
.device_token("fox_dt-...")
.start()
.expect("Failed to start remote access gateway");

If the device token is not provided, the gateway reads it from the FOXGLOVE_DEVICE_TOKEN environment variable.

For more options, see the Gateway reference.

Logging messages

The gateway is a sink: any messages logged to a channel in the gateway's context are streamed to connected clients. Log to channels as you would for any other sink. See the logging messages guide for how to define channels and schemas.

Status messages

You can publish a status message to the app, which will be displayed in the Problems panel. Use this to communicate warnings and errors within the app.

handle.publish_status(foxglove::remote_access::Status::error("Error!"));

If you provide a status ID when publishing, you can later remove the status from the Problems panel by calling remove_status.

Capabilities

You can augment the gateway with additional capabilities that unlock more features in the app. To advertise capabilities, provide them when constructing the gateway.

use foxglove::remote_access::{Capability, Gateway};

let handle = Gateway::new()
.capabilities([Capability::ClientPublish])
.supported_encodings(["json"])
.start()
.expect("Failed to start remote access gateway");

Handling messages from the app

The Publish panel and Teleop panel can send messages to the gateway from the app.

In addition to advertising ClientPublish support, declare the encodings you support and implement a listener to receive the messages.

use foxglove::ChannelDescriptor;
use foxglove::remote_access::{Capability, Client, Gateway, Listener};
use std::sync::Arc;

struct MessageHandler;
impl Listener for MessageHandler {
fn on_message_data(
&self,
client: &Client,
channel: &ChannelDescriptor,
message: &[u8],
) {
let json: serde_json::Value =
serde_json::from_slice(message).expect("Failed to parse message");
println!(
"Message from client {} on topic {}: {json}",
client.id(),
channel.topic()
);
}
}

let handle = Gateway::new()
.capabilities([Capability::ClientPublish])
.supported_encodings(["json"])
.listener(Arc::new(MessageHandler))
.start()
.expect("Failed to start remote access gateway");

The Listener trait provides additional callbacks for tracking subscriptions and client-advertised channels. For more detail, see the remote access example.

Next steps