← João Rodrigues
Engineering case study · Remote access · São Paulo

OpenRemote

A secure remote-control platform — Electron, WebRTC, native Android — and the production incident that rewrote how I back things up.

RoleArchitect, developer, release manager, on-call
StackElectron · WebRTC · Node.js + Express + WS · SQLite · Kotlin / Jetpack Compose · Swift
PlatformsWindows · macOS · Linux desktop · iOS · Android · Windows service agent
Statusv3.1.31 in production · multi-tenant · CI on every push
What it is

TeamViewer, but ours, and auditable

OpenRemote is secure machine-to-machine remote control: screen, mouse, keyboard, clipboard and file transfer between a controlled machine and an operator, over the public internet, with the operator's organisation isolated from every other one on the server.

It is cross-platform on both ends. The controlled machine runs on Windows, macOS and Linux. The operator connects from a desktop app, from iOS, or from a native Android client in Kotlin and Jetpack Compose. On Windows there is also a dedicated agent that installs as a system service and can run inside the Winlogon session — which means a machine can be reached at the login screen, before any user has signed in.

I built it because supporting client machines remotely is part of what my company does, and the commercial options put a third party in the middle of customer systems. The interesting part is not that it works — it is what a real-time interactive protocol forces you to get right.

Architecture

Signaling through the server, media peer-to-peer

The server brokers identity and introductions; it does not sit in the media path unless the network forces it to. That single decision is what keeps a remote session usable across a domestic connection in Brazil.

Signaling server Node · Express · WS · TURN SQLite: users · machines · audit Controlled machine Electron client Windows · macOS · Linux Operator Electron admin desktop · iOS · Android one WebSocket machineToken one WebSocket JWT · 2FA · 60 s PIN H.264 video · adaptive 60/30/25 fps by RTT ordered channel: clicks · keys · files unordered channel: mouse movement peer-to-peer — TURN relays only when the NAT refuses
Identity and introductions go through the server; pixels and input do not. The two data channels are the design decision worth the diagram — they carry different traffic under different reliability guarantees.
Design decisions

Three calls that make it feel like a local machine

Transport

Two data channels, two reliability guarantees

Everything shares one WebRTC connection, but not one channel. Clicks, keystrokes and file bytes go over an ordered channel, because a keystroke arriving out of sequence corrupts what the user typed. Mouse movement goes over an unordered one, because a mouse position that arrives late is worthless — the only coordinate that matters is the newest.

Why it mattersForcing pointer motion through an ordered channel makes the cursor lag behind the hand under packet loss, and no amount of bandwidth fixes it. This is the same reasoning as choosing between TCP and UDP for a video contribution feed.

Perceived latency

Draw the cursor locally, at zero milliseconds

The remote machine's real cursor is a round trip away. Instead of waiting for it to come back in the video, the controller draws its own cursor immediately at the position the operator is pointing at, and the video catches up underneath.

Why it mattersPerceived responsiveness is a different quantity from measured latency, and the user only experiences the first. Fixing the perception cost nothing on the wire.

Trust boundaries

Secrets live in the operating system's vault

Auth tokens are encrypted at rest by the platform keystore — Keychain on macOS, DPAPI on Windows, libsecret on Linux — rather than sitting in a config file. TLS certificates are verified on the signaling socket, with the bypass available only in development builds. File transfer validates paths against traversal. Access needs a PIN that expires in 60 seconds, and every session is written to an audit log.

Why it mattersA remote-control tool is the highest-value target on a customer's network. If it is compromised, it is not one machine that falls.

Multi-tenancy

Isolation written down as an invariant, not a hope

The server hosts more than one customer organisation, so the interesting bug class is not "does the feature work" but "can data from tenant A ever reach tenant B". Three of the project's architecture decision records exist purely to fence that off: tenant isolation is treated as an invariant; a machine does not migrate between companies; and re-registering an existing machine requires proof of possession rather than just its identifier.

Writing those down as ADRs — context, decision, alternatives rejected, consequences — is what keeps a later change from quietly undoing them. It is also what makes the reasoning reviewable by somebody who was not there.

Incident · 6 August 2026

The backups were inside the machine that died

This is the part of the project I would most want a hiring engineer to read, because it is the part that changed how I work.

A server deploy followed the checklist exactly. Back up the application directory: done. Copy the database before touching anything: done. Both copies were written inside the VM.

That night the VM failed. The Proxmox restore brought it back — to a snapshot fifteen days old, and it took both backups with it, because they lived on the disk being rolled back. The code survived, because code lives on GitHub. The data did not: a customer organisation record, its administrator, its registration codes, and fifteen days of audit log.

The part nobody saw coming was worse. The nightly Proxmox backup had not produced a usable file since 22 July. Nothing alerted. For fifteen days everyone believed there was a restore point, and there was not.

BEFORE — 5 AUGUST virtual machine app + db backup 1 backup 2 rollback of the VM erases all three · 15 days of data gone AFTER — THE RULE virtual machine app + db .sql dump another machine ~2 s · ~500 KB rollback of the VM leaves the backup untouched
The whole lesson in one picture: a copy that shares a failure domain with the original is not a backup. What changed is not the frequency or the tooling — it is which machine the file lands on.

The four rules that came out of it

Generalised

If a piece of data exists in exactly one place, it is already lost — you just do not know the date yet. The rule is not about databases. It applies to project media on a single RAID, to the only copy of a show file on the operator's laptop, and to a shoot that has not left the camera cards.

Release engineering

Rollback is a precondition, not a contingency

A previous upgrade bundled several changes into one release, went out without a tested way back, crashed both the server and the clients, and cost days. The version was withdrawn and the code preserved on its own tag and archive branch so its features could be reintroduced one at a time.

What replaced it is a checklist that runs before anything is touched: the current version must already be tagged and pushed; the commit currently running in production is recorded; the current installers must exist and be retrievable; and each feature is one commit, deployed and verified on its own before the next one starts. The new version tag is created only after the deploy is confirmed stable.

None of this is novel — it is standard practice in any serious release process. What is worth noting is that it was adopted the way most teams adopt it: after an outage, written down as a decision record so the reasoning outlives the memory of the bad night.

Numbers

Where the project stands

5platforms supported
6architecture decision records
3CI workflows on push
60fps adaptive, LAN
2FATOTP + JWT + bcrypt

The controlled machine runs on Windows, macOS and Linux; the operator can be at a desktop, on iOS or on Android; and a separate Windows agent installs as a service and can launch inside the Winlogon session, so a machine is reachable before anyone has logged into it — the case that matters most when you are supporting a client remotely and there is nobody in front of the computer. Alongside the code, the repository carries runbooks, incident write-ups and handoff notes, so a person who was not present can pick the project up from what is written rather than from what I remember.

How this was built

A large share of this code was written by AI. That is disclosed on purpose.

The implementation is heavily AI-assisted. The architecture, the decisions, the reviews, the integration, the releases and the 3 a.m. accountability are mine. I am telling you this on the case study rather than waiting for the interview, because in 2026 the interesting question about an engineer is no longer whether they typed every line — it is whether they can stay responsible for a system that now produces code faster than any human can read it.

Mine

The idea. The rules the build has to follow — programming and commit discipline, the test harness, the security model, and how the product is organised for market. Choosing the technology. Coordination, and the instructions that direct the work. Testing against real signal, real audio and real hardware, and the fine tuning until it behaves. Deciding what ships and what gets rolled back.

Delegated

The implementation itself — the code, the platform plumbing, the first drafts of documentation.

Directing a model safely into production needs more process than writing the code by hand, not less — because the model has no memory between sessions, no stake in the outcome, and infinite confidence. So the repository carries the things a human team would carry in their heads: a written briefing every session reads before touching anything, decision records so a model with no memory cannot quietly re-litigate a choice that was already made and paid for, runbooks, and handoff notes.

The 3.2.0 outage is the clearest example of the specific risk. AI makes it trivially cheap to generate ten changes at once, and that is exactly what went out: several features bundled into one release with no tested way back. It crashed the server and the clients and cost days. The rule that replaced it — one feature, one commit, deployed and verified before the next one starts — is not general good practice for its own sake. It is a brake designed against the failure mode that AI-assisted velocity creates.

Calls that were mine — concretely

  • Rollback as a precondition, one change per commit. Written as the project's first architecture decision record after a bundled upgrade crashed the server and the clients. It is a brake against exactly the failure mode that fast code generation creates.
  • Backups leave the machine. The rule, and the three that go with it — listed not presumed, an unverified automatic backup does not exist, portable .sql format — came out of the incident above and are recorded so they outlive my memory of that night.
  • A commercial requirement the implementation had missed. The product cannot ask a customer to change their firewall. The relay was offering only port 3478, so a machine behind a corporate firewall that allows just 80 and 443 outbound — precisely the customer who most needs remote support — could not connect, while the commercial alternatives fall back to TCP 443 and get through. A TLS relay on 443 is now the last step of the ICE list. The code is done; the DNS record, the certificate SAN and the HAProxy SNI rule are not, and until they are, the release changes nothing.
  • Tenant isolation as an invariant. Three of the six decision records exist only to fence off the question "can data from one customer ever reach another", including the rule that re-registering a machine requires proof of possession rather than just its identifier.
  • Design reversals, and decisions the implementation was told not to make. The tenancy document records rules I closed on a given date — including one that reversed an earlier design — and carries a section titled "pending decisions: do not implement on your own". Setting the boundary of what a model is allowed to decide is part of the architecture, not a footnote to it.
  • Validation against real hardware. Single click, window drag, double click and keyboard, tested live on the machine; latency measured in a real session from an Android client; a screen-lock behaviour and an app-restart requirement that only surfaced from using the thing as a customer would.

The record is in the repository, not just in this page. OpenRemote's own documents attribute decisions by name and date — "premise closed with João on 5 August", "decision of 26 July, supersedes the previous understanding", and a section of pending decisions marked do not implement on your own. That is what makes the disclosure above checkable rather than a claim: anyone with access to the repository can see which half of the work was mine.

The line I hold

I do not write every line. I do read every line I integrate. Integrating code you do not understand builds a system only the model can maintain, and that is a trap with a delayed invoice — it looks like speed for about six months. Where I am still working: measuring instead of estimating, and closing the gap between what I can review and what I could write unaided.

João Rodrigues

Broadcast and live-event engineer, São Paulo, Brazil. Partner at VENG (Vídeo Engenharia) — professional AV, broadcast technology and technical training. I build real-time systems for environments where the deadline is the doors opening: live video, remote operations, and AI applied to production workflows.

Consulting

Real-time video, remote operations and AI in AV and broadcast workflows.

Integration projects

Delivered through VENG — live captioning, remote support platforms, production automation.

Training & speaking

Applied AI and streaming engineering for AV professionals, in Portuguese, English or Spanish.

International collaboration

Remote technical collaboration with teams outside Brazil — contract projects, partnerships and joint work across time zones.

joao@video.eng.br  ·  +55 11 99602-1111

September 2026
Every figure, incident and decision on this page comes from the project's own repository.