Direct Connection (P2P)
By default, video travels browser → Adamo router → robot. A direct connection (peer-to-peer) sends it straight between the browser and the robot over WebTransport, cutting out the router hop for lower latency.
It’s additive and automatic: the browser uses the direct path when one is available and silently falls back to the relay when it isn’t — so nothing breaks, you just get lower latency where the network allows it.
When it’s used
Section titled “When it’s used”A direct connection is used only when all of these hold — otherwise the session stays on the relay, which always works:
- The robot serves P2P (see below) and its firewall allows the direct port (see Open the robot’s firewall).
- A direct network path exists between your browser and the robot: the same LAN, the same Tailscale tailnet, or a robot with a publicly reachable IP. Robots behind symmetric CGNAT (for example Starlink) usually can’t be reached directly and stay on the relay.
- You’re on Chrome / a Chromium browser. The direct path pins a self-signed certificate, which only Chromium supports; Safari and Firefox fall back to the relay.
Enable it on the robot
Section titled “Enable it on the robot”The robot has to opt in. Add one call to the robot you already have (see Quickstart):
import adamo
robot = adamo.Robot(api_key="ak_...", name="my-arm", p2p=True)robot.attach_video("main", device="/dev/video0")robot.run()use adamo::Robot;
fn main() -> adamo::Result<()> { let mut robot = Robot::new_default("ak_...", Some("my-arm"))?; robot.enable_p2p(17447)?; // the UDP port operators open in the firewall robot.attach_v4l2("main", "/dev/video0", 1280, 720, 30, 4000, false)?; robot.run()}adamo_robot_t *robot = adamo_robot_new_default("ak_...", "my-arm");adamo_robot_enable_p2p(robot, 17447); // the UDP port operators open in the firewall// ... attach video ...adamo_robot_run(robot);If you run the adamo-network binary against a YAML config, pass --p2p (and optionally --p2p-port, default 443):
adamo-network --config robot.yaml --p2pOr set it in the config file:
p2p: truep2p_port: 443The P2P listener is additive — the robot keeps its normal relay connection, so operators who can’t reach it directly still see video over the relay.
Open the robot’s firewall
Section titled “Open the robot’s firewall”A direct connection is an inbound connection to the robot, so the robot’s own firewall has to let it in. The relay connection is outbound and unaffected, which is why a robot with a closed firewall still works fine over the relay — it just never goes direct.
Hosted robots listen for direct connections on UDP port 17447. The SDK prints the port it ended up on at startup:
adamo: direct connections listen on UDP port 17447 (and TCP 17447 for offline pairing); open the UDP port in the robot's firewall for operators on its networkIf 17447 is already taken on the host (for example by another robot process), the SDK tries 17448, 17449, and so on, so check the printed port rather than assuming. To pin a different port, set ADAMO_DIRECT_PORT in the robot’s environment.
On Ubuntu / Debian with ufw, allow the port:
sudo ufw allow 17447/udpTo limit the rule to operators on the robot’s LAN, restrict it to your subnet instead (adjust 192.168.1.0/24 to match your network):
sudo ufw allow proto udp from 192.168.1.0/24 to any port 17447Other firewalls follow the same idea — allow inbound UDP on the robot’s direct port:
# firewalld (Fedora / RHEL)sudo firewall-cmd --permanent --add-port=17447/udp && sudo firewall-cmd --reload
# nftables / iptablessudo iptables -I INPUT -p udp --dport 17447 -j ACCEPTOffline pairing (robots running without an Adamo account, see the SDK’s Robot.local) also uses TCP on the same port; open 17447/tcp as well only if you use it.
A few things that look like a firewall problem but aren’t:
- The browser’s “local network access” prompt (Chrome) only lets the page attempt a local connection. Allowing it does not open anything on the robot — the robot-side rule above is still required.
- Wi-Fi client isolation. Many guest and office networks stop devices on the same Wi-Fi from talking to each other at all. If a wired laptop connects directly but a phone on the Wi-Fi stays on the relay with the firewall open, this is usually the cause. Connect both through the same switch or use Tailscale.
- Different networks. The direct path needs a route from the browser to the robot; across networks that means Tailscale or a publicly reachable IP, not a firewall change.
To check which path you’re on, see Use it in the browser.
Use it in the browser
Section titled “Use it in the browser”Nothing to configure. Open the robot on operate.adamohq.com in Chrome and it connects directly when it can.
- Chrome may show a one-time “allow local network access” prompt the first time — click Allow (it persists for the site).
- To confirm you’re direct, open the Control Panel latency breakdown: the path shows
you ↔ robot (direct)instead of the router hops.
Best results: Tailscale
Section titled “Best results: Tailscale”The most reliable way to get direct connections across different networks is to put both the robot and the machine running your browser on the same Tailscale tailnet. The robot then advertises its tailnet address as a candidate, and the connection works regardless of NAT — including symmetric CGNAT / Starlink, where a raw direct connection can’t be established. The robot must be reachable on its P2P port over the tailnet’s ACLs.
Force the relay
Section titled “Force the relay”Direct connections fall back to the relay automatically, but you can also force relay-only for a browser session — run this in the browser console and reload:
localStorage.setItem('adamo_p2p', '0')- Direct connections are authenticated and org-scoped, and teleop minutes are metered, exactly like the relay.
- If a direct connection drops mid-session, video continues over the relay.
- P2P carries video (and teleop) directly; discovery, fleet presence, and the robot list still go through the Adamo network as usual.