Linux on board. Cameras or a screen. Behind someone else's network, far from the people who need to see it, drive it and fix it. If that's your product, Fjarr is how you reach it.
Live video, the desktop, a terminal and a direct network link — peer-to-peer, encrypted end to end, through firewalls and carrier NAT with no ports to open. Built into your machine, your backend and your dashboard.
Robots are where we started; vehicles, farm machinery and field gateways have the same shape. Open-core (AGPL-3.0) libraries you self-host, plus a managed cloud when you'd rather not. Fjärr is Swedish for "remote".
Read the docsGitHubwget -qO- https://get.fjarr.io | sudo shRarely nothing. Usually a patchwork that kind of works:
scp through two hops to get one log out.Each piece works on a good day. Together they're slow to support, hard to secure, and a different login every time. Fjarr replaces the patchwork with one connection per machine that carries all of it.
We've built this stack three times before, inside robot companies — what we kept and what we didn't.
Fjarr embeds into what you already run. Your backend stays in charge of who may reach what; video and data go straight between the machine and the person looking at it.
The fjarr-server sidecar, beside a backend in any language. Your backend signs grants and receives webhooks. It never touches the media.
Embed libfjarr (C++/GStreamer) in your software, or run the fjarr-agent daemon beside it. Hardware encode.
@fjarr/core + @fjarr/react in the dashboard you already have — standard WebRTC, any modern browser. Headless-first: your design system wins.
// any JWT library, any language
const grant = jwt.sign({
aud: "fjarr",
tenant: "acme",
robot_id: "robot-024",
operator: { id: user.email, label: user.name },
capabilities: [
{ name: "fjarr.camera" },
{ name: "fjarr.desktop" },
],
}, GRANT_KEY, { algorithm: "HS256", expiresIn: "5m" });import {
createFjarrClient, FjarrProvider, DesktopView,
} from "@fjarr/react";
const client = createFjarrClient({
serverUrl: "wss://fjarr.acme.example",
grant: (id) => myApi.fjarrGrant(id), // your grant endpoint
});
<FjarrProvider client={client}>
<DesktopView
session={client.sessions.open("robot-024")} />
</FjarrProvider>The contract between the pieces: interfaces · architecture
Many cameras, one encode fanned out to every viewer, adaptive bitrate. Cameras that already speak H.264 cost no encoder at all.
A shell in the browser, over the same session. Off until configured, audited when opened.
A routable address for one machine: ssh, scp, ROS 2, your debugger, your vendor tools.
The live media pipeline as a graph, on the machine and in your dashboard.
The machine's own desktop in your dashboard, unattended after a reboot, every monitor.
Hear the machine's microphones, talk through its speaker, push-to-talk.
The machine's files as a drive, and resumable transfers that survive a flaky LTE link.
Typed sensor streams with change-triggered updates, and logs that store and forward when offline.
Versions, host metrics and error rates across the fleet.
A/B updates with rollback and streamed binary diffs.
available: in a release · in development: running on main · planned: on the roadmap. We don't market what doesn't run.
Every number below was measured on named hardware at a milestone gate, against a written budget. Click one for where and when.
The desktop your machine already has, in your dashboard — no tool for the end customer to install. in development
Driving is one operator at a time, and never handed over on idle. Taking it over releases every input before anyone new can move it.
Desktop control frees five seconds after the holder's last input, or anyone can take it at once — and the holder is told.
If an operator's connection dies, their held keys and buttons are released and their control lapses. A machine is never left stuck.
A view-only grant can never take control or read the clipboard — for the customer, the auditor, the second pair of eyes.
Each of these ships with a regression test (safety behaviors). How control works: security · remote desktop.
Every team keeps a list of things it occasionally needs to do on a machine that nobody will ever write a protocol for. The network link is the escape hatch: an authorized operator gets a routable address for one machine, inside the session that already carries its video — and the tools they already use just work.
$ fjarr-connect login # your company's sign-in, in the browser
$ fjarr-connect robot-024
robot-024 100.70.118.224 mtu 1184 up in 0.8 sBridge its bus to a virtual interface; SavvyCAN or the vendor's tool sees it live.
cannelloni -I vcan0 -R <machine> -r 20000 -l 20000
candump vcan0VS Code Remote-SSH for the editor, gdbserver for a process that's already running.
(gdb) target remote <machine>:2345DDS discovery crosses the link, so the CLI and rviz2 just see the robot's graph.
ros2 topic list
rviz2Your docker client, the machine's engine — logs, exec, restarts.
DOCKER_HOST=ssh://user@<machine> \
docker logs -f my-nodeAnd ssh, scp, RDP, your file manager, Wireshark, a serial port, Modbus — more ideas.
Connect to two machines at once and they still can't see each other. Every link ends at you and nothing is forwarded between them — there's no path to leave open by mistake.
You already decide who may reach machine 42. The link is that same decision, in the same grant, in the same audit log. No second identity system to run beside yours.
The link exists while a session does, then it's gone. Nothing becomes permanently reachable, and there's no extra daemon on the machine to install, update or license.
It deliberately isn't a mesh: no shared network, no machine-to-machine routing, no always-on reachability. It's developer and support access — the design says exactly where that line sits.
Media and data are encrypted between the machine and the viewer. When a relay is needed, it only ever forwards ciphertext.
Access is a short-lived grant your backend signs, scoped per capability. The machine re-checks it against its own config, so a grant can't switch on what you didn't.
Relay credentials are minted per session and expire with the grant. Nothing long-lived is compiled into a frontend.
The machine dials out. It never asks the customer's router to open a port, and their IT department has no inbound rule to approve.
The agent runs as its own unprivileged user. Desktop access is narrowed to the screen — not the microphone.
A signed apt repository, and container images signed with an SBOM.
A second viewer costs fan-out, not another encoder.
Video encodes on the iGPU, so the discrete GPU stays with perception — the job the machine exists to do.
A transfer saturating the link leaves the camera beside it at full frame rate.
The whole threat model is public: security. Encoders and hardware: supported platforms.
The live pipeline graph, with rates and drops, on the machine and in your dashboard — readable by a support engineer or an AI coding agent. Introspection
sudo fjarr-agent --check lists every configured source and backend, and gives the one-line fix for anything missing. Install & drivers
fjarr-agent drivers detect matches the hardware actually attached against a catalog and names the package each needs. Install those, and nothing else lands on the machine.
| Free & open | All three libraries, the signaling sidecar, the network link and its client, the protocol, the docs, the demos. Genuinely usable alone, self-hosted forever. |
| Fjarr Cloud | Managed signaling and relays, fleet observability, staged OTA campaigns, SSO — metered by machines, session-minutes and relay bandwidth. Same contract as self-hosting: switch either way, no rework. |
| Licence | AGPL-3.0, or a commercial licence when AGPL doesn't fit your product. Nothing we ship depends on GPL code, and the build environment refuses it. |
The whole business plan is a public document: product strategy.
The specs are the product's source of truth, and they're all public.
Read the docsGitHubRoadmap