Skip to content

Controlling a remote machine

malac-control, the computer and browser control service, does not have to run where malac runs. Start it on a remote machine that has a desktop, connect malac to it, and the agent’s computer_* and browser_* tools act on that machine: its screen, its mouse and keyboard, and its browsers.

This is how malac tests GUIs and websites on other machines: a Mac mini in a rack, a Linux desktop VM, a test laptop on the office network.

Runs on your machine Runs on the remote machine
malac: the agent, the model requests, the session transcripts malac-control serve: screenshots, mouse, keyboard and browsers
read, edit, bash and the other built-in tools, on your files Browser profiles in the remote’s ~/.malac/browser/
Your configuration and permission rules The remote’s ~/.malac/control.toml

So bash still runs commands on your machine, and a URL such as http://localhost:3000 in browser_navigate means port 3000 on the remote machine.

Install malac there as usual (Installation); you only need the malac-control binary on that side, and it is installed next to malac. Use the same malac version on both machines.

macOS. Desktop control needs the Screen Recording (Screen & System Audio Recording on macOS 15 and later) and Accessibility permissions, and macOS grants them to the app that started malac-control. The reliable way is to start the service from a terminal inside the remote machine’s desktop session, for example over Screen Sharing, and grant that terminal both permissions in System Settings → Privacy & Security. Check from a terminal there with malac-control permissions, which shows what is granted and to which app; malac-control permissions --request shows the macOS prompts and opens System Settings at what is missing. Add that app with + if it is not listed. See macOS permissions. Browser control needs neither permission.

Linux. Run the service inside the remote user’s graphical session, so that it can reach the display. A source build of malac-control needs the X11 and D-Bus headers (see Installation).

Only browser control needed? Set computer.enabled = false in the remote’s ~/.malac/control.toml, and the desktop permissions do not matter.

malac-control serve requires a token. Without one it makes up a random token and writes it to ~/.malac/control.json, but that file is on the remote machine, where your malac cannot read it. So pick a token and give it to both sides:

# on the remote machine, in a terminal in its desktop session
export MALAC_CONTROL_TOKEN="$(openssl rand -hex 24)"
echo "$MALAC_CONTROL_TOKEN" # copy this to your machine
malac-control serve # listens on 127.0.0.1:7878
Option Default Meaning
--host <ip> 127.0.0.1 Address to listen on. An IP address, such as 0.0.0.0 or a private network address.
--port <port> 7878 Port
--token <token> random The token clients must send. Prefer the MALAC_CONTROL_TOKEN environment variable, which does not show up in process lists.

The service logs to its terminal (RUST_LOG=debug for more), and when it stops, it closes the browsers it launched.

The service speaks plain WebSocket: the token is checked on every connection, but nothing is encrypted. Do not expose port 7878 to the internet. Use one of these:

An SSH tunnel (recommended). Keep the service on 127.0.0.1 on the remote machine and forward the port from your machine:

ssh -N -L 7878:127.0.0.1:7878 user@remote-host

Your malac then connects to ws://127.0.0.1:7878 on your own machine, and SSH carries the traffic encrypted.

A private network. On a VPN or an encrypted overlay network you trust, listen on the remote’s private address instead and connect to it directly:

malac-control serve --host 10.0.0.12

On your machine, give malac the address and the name of a variable that holds the token, in your user config (~/.malac/config.toml; a project’s config cannot set [control]):

[control]
url = "ws://127.0.0.1:7878" # the SSH tunnel; or ws://10.0.0.12:7878
token_env = "MALAC_REMOTE_CONTROL_TOKEN"
export MALAC_REMOTE_CONTROL_TOKEN=... # the token from step 3
malac mcp # checks the connection from this shell
● control (28 tools)

Then restart the daemon from that same shell, so it reads the new [control] settings and has the token variable in its environment (the daemon keeps the environment it was started with, and connects the control service once when it starts):

malac daemon restart

With [control] url set, malac connects only to that address: it no longer reads the local ~/.malac/control.json or spawns a local malac-control. Remove url and restart the daemon to go back to local control. If the connection fails, malac mcp shows the error under control; in the terminal UI, the View menu says the control service is not connected and the status line shows control unavailable.

To use the remote machine for one-off runs only, put the [control] table in a profile and pass it to a run in its own process:

[profiles.remote.control]
url = "ws://127.0.0.1:7878"
token_env = "MALAC_REMOTE_CONTROL_TOKEN"
malac -p --profile remote "check the sign-up page on the remote machine"

Browser profiles belong to the service, so they live on the remote machine: configure them in its ~/.malac/control.toml, and their data directories are in its ~/.malac/browser/. Everything in Browser profiles applies there:

# ~/.malac/control.toml on the remote machine
[browser.profiles.staging]
[browser]
headless = false # show the windows on the remote desktop, so you can watch over Screen Sharing

Log in once inside a profile on the remote desktop, and the session stays in that profile. List the remote’s profiles with malac-control profiles on that machine.

Worked example: test a web app’s UI on a remote desktop

Section titled “Worked example: test a web app’s UI on a remote desktop”

Say the remote machine runs a development build of your web app on port 3000, and you want malac to check the sign-up flow in a real browser there.

  1. On the remote machine, start the app and the control service in its desktop session:

    npm run dev # the app, on http://localhost:3000
    MALAC_CONTROL_TOKEN=... malac-control serve
  2. On your machine, open the tunnel and start malac:

    ssh -N -L 7878:127.0.0.1:7878 user@remote-host &
    malac --name signup-test

    With [control] url in your user config (step 5), every tab of the daemon drives the remote machine.

  3. Ask for the test, and say where things are:

    Using the browser on the remote machine (profile "staging"), open
    http://localhost:3000/signup. Fill in the sign-up form with a made-up
    test user, submit it, and check that the welcome page appears. Take a
    screenshot at each step and report anything that looks broken: overlapping
    elements, cut-off text, error messages, or controls that do not respond.
  4. Watch it work. malac uses browser_navigate, reads the page with browser_snapshot, fills the form with browser_type and browser_click, and checks the result with browser_screenshot. Snapshots and screenshots are read-only and never ask; navigation, typing and clicks follow your permission mode, so approve them or pre-approve the browser:

    [permissions]
    allow = ["browser_*"]
    deny = ["browser_eval", "computer_*"] # no JavaScript, no desktop, for this kind of test
  5. For a native app instead of a web page, use the desktop tools: ask malac to take a computer_screenshot, open the app with computer_open_app, and click and type at positions from the screenshot. Those need the desktop permissions from step 2.

If the model you use cannot read images, set a vision_model and mark the model vision = false, so screenshots are described in words; see Computer and browser control.

  • The token is the only thing standing between the network and the remote machine’s mouse and keyboard. Keep it out of prompts and repositories; use environment variables on both sides.
  • Keep the service on 127.0.0.1 and use an SSH tunnel, or a private network you trust. The WebSocket is not encrypted.
  • Give the remote machine a dedicated user account and test-only logins. The agent can do whatever that desktop session can.
  • Deny the tools you do not need, such as computer_* for browser-only tests or browser_eval.
  • Stop the service when you are done; it closes the browsers it launched.

malac is created by lobirus and released under the MIT license. About malac