Repository navigation
Controlling a remote machine
Run malac-control on a remote machine with a desktop and connect malac to it, to test GUIs and websites there.
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.
Tip
Looking for the other way around? This guide keeps the agents on your machine and moves only the desktop. To run the agents themselves on a server and drive them from your laptop or phone, see Remote access.
| 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-hostYour 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.12On 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 restartWith [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 SharingLog 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.
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.
-
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 -
On your machine, open the tunnel and start malac:
ssh -N -L 7878:127.0.0.1:7878 user@remote-host & malac --name signup-testWith
[control] urlin your user config (step 5), every tab of the daemon drives the remote machine. -
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. -
Watch it work. malac uses
browser_navigate, reads the page withbrowser_snapshot, fills the form withbrowser_typeandbrowser_click, and checks the result withbrowser_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
-
For a native app instead of a web page, use the desktop tools: ask malac to take a
computer_screenshot, open the app withcomputer_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.1and 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 orbrowser_eval. - Stop the service when you are done; it closes the browsers it launched.
Getting started
Tutorials
- Tutorials
- Tutorial 1: Your first task
- Tutorial 2: Delegating to subagents
- Tutorial 3: Automating a browser
- Tutorial 4: Long-term memory with Milvus
- Tutorial 5: Running malac unattended
- Tutorial 6: Customizing malac
- Tutorial 7: Running malac on a server
Using malac
- Workspaces, tabs and the daemon
- The terminal UI
- Plain mode and scripting
- Sessions
- Jobs
- Permissions and autopilot
- Subagents
- Memory
- Project context
- Plugins and marketplaces
- Computer and browser control
- Web UI
- VS Code
- Remote access
- Mobile (preview)
- MCP servers
- Configure malac by asking
- Updating
Reference
- CLI
- Slash commands
- Configuration
- Tools
- Control service tools
- JSON events
- Protocol
- Environment variables
Guides
- Using an LLM gateway
- Running models locally
- Vector memory with Milvus
- Controlling a remote machine
- Writing agent types, skills and commands
- Security model
- Things to know
Project