~/vimma $ less docs/vimma-ctl.md

page 2/5 · docs/vimma-ctl

Scripting with vimma-ctl

vimma-ctl controls a running Vimma from the command line: send it keys, read its state, open a URL. Use it from scripts or window-manager bindings. It is off until you turn it on.

before you turn it on

Turning it on gives full control of the browser to any program that runs as you: it can type into your logged-in sites, change any setting and open local files. Leave it off unless you use it.

Set it up

  1. In about:config, set vimma.control.enabled to true. It starts at once.
  2. Build the client: cargo build --release in tools/vimma-ctl/ of Vimma's checkout. Put target/release/vimma-ctl on your PATH.

Examples

vimma-ctl state                    # mode, workspace and tabs
vimma-ctl state --json             # the same, as JSON
vimma-ctl send-keys "ctrl+b ?"     # as if typed: opens the key list
vimma-ctl send-keys esc
vimma-ctl open https://example.com # in a new tab

In Hyprland, a key that shows Vimma's key list:

bind = SUPER, slash, exec, vimma-ctl send-keys "ctrl+b ?"

Keys go to the most recently focused Vimma window, exactly as if you typed them.

Details

Everything below is Vimma's own user guide for this topic, in full: every edge case and exception. You do not need it to get started.

vimma-ctl drives a running Vimma from a shell, a window-manager binding or a test: it sends keys, reads the state and opens URLs. It is for when you want your browser to be part of your scripts. It talks to the browser over a local control socket, which is off by default.

  1. Turn the socket on: in about:config, set vimma.control.enabled to true. It starts at once and writes vimma-control.json into your profile directory. Setting it back to false stops it and deletes the file.

  2. Build the client (Rust, no dependencies): cargo build --release in tools/vimma-ctl/; the binary is tools/vimma-ctl/target/release/vimma-ctl. Packages will ship it later.

  3. Use it:

    vimma-ctl state                    # mode, pending keys, workspace, tabs
    vimma-ctl state --json             # the same, as JSON
    vimma-ctl send-keys "ctrl+b ?"     # typed as if on the keyboard: opens the help overlay
    vimma-ctl send-keys esc
    vimma-ctl open https://example.com # a new tab; only http, https, file and about URLs
    vimma-ctl reload-theme             # read the Omarchy theme now (the hook's fallback)

    It finds your default profile itself; pass --profile DIR (or set VIMMA_PROFILE) for another one. In Hyprland: bind = SUPER, slash, exec, vimma-ctl send-keys "ctrl+b ?".

send-keys presses the keys in the most recently focused Vimma window, exactly as typing them would: Vimma's bindings, Firefox's shortcuts (ctrl+t, ctrl+q) and the page all see them. Keys meant for the page reach it only while that window has focus, as with a real keyboard.

Security. Turning the socket on hands full control of the browser to whoever has the token: they can type into any page (including your logged-in sites), open about:config and change any setting, and quit Vimma. open loads with the system principal, like a URL given on the command line, so it can open local files (file:) and privileged pages such as about:config; only javascript: and data: are refused. Leave the pref off unless you use it.

What keeps others out:

  • The socket listens on 127.0.0.1 only, on a random port. Web pages cannot speak to it (they cannot read the token, and an HTTP request is rejected).
  • A client must send the token from vimma-control.json: 256 random bits, new at every start, compared in constant time. The file is readable by you alone (mode 0600), and vimma-ctl refuses a file anyone else can read. Other users on the machine cannot get it; anything running as you can, just as it can already read your profile's cookies and history.
  • The browser proves it knows the token first: vimma-ctl sends only a random nonce, and sends the token only after the browser has answered with an HMAC of that nonce made with the token. If Vimma crashed and another program took its old port, that program gets the nonce and nothing else: no token and no command (vimma-ctl exits 4). It can still make vimma-ctl wait until its timeout.
  • A stale file (Vimma crashed or was killed with kill -9 or SIGTERM) is removed at Vimma's next start, whether or not the socket is on. Until then, on Linux, vimma-ctl sees that the process named in it has exited and says Vimma is not running; if that process number has been reused, the handshake above still protects you.

Limits against a local denial of service: at most 8 authenticated connections (the next is told "busy"); at most 2 connections still authenticating, a new one pushing out the oldest, so idle connections cannot lock vimma-ctl out; 5 s to authenticate; an idle connection closes after 60 s; lines over 1 MiB and clients that stop reading are disconnected. These are limits, not a defence against an active attack: a program of any local user that connects fast enough keeps pushing vimma-ctl out before it authenticates, and any program running as you can keep the 8 slots busy. Turn the pref off to stop either.

With the help overlay open, every key goes to the overlay, ctrl+q included, as when typing; send esc first (vimma-ctl send-keys "esc ctrl+q").

The guide at the top is written for this site; the details are built from Vimma's docs/guide.md and bindings.md at 1050bcc (2026-10-10).

Keys on this site

Keyboard shortcuts for this website
j kscroll down, up
d uhalf a page down, up
gg Gtop, bottom
/search the docs, or filter the keys
] [next, previous docs page
g hgo home
g dgo to the docs
g kgo to keys
g igo to install
g ago to about
?this help; Esc closes it

In Vimma, ctrl+b ? lists every binding.