Elyra
Elyra The coding agent eTerm The terminal that knows where each command ends Huskarl AI colleagues on your own Mac Starf An activity monitor for Apple silicon that never invents a number etrans An SSH and SFTP client for macOS Elyra VM Virtual machines for macOS, Linux and Windows on your Mac Litr A small, native web browser for macOS Notr A notebook for macOS e The native code editor Elyra Grove Native local development environment Askr The real server for Laravel & PHP Elyra Framework Rust + Svelte 5 framework for desktop apps Elyra Conductor Local project conductor Refr Local-first PDF workspace for macOS Elyra Workspace A desktop workspace for coding agents Elyra SQL Server MySQL-compatible SQL server in Rust Elyra Félagi Agents as teammates on one board Elyra SQL Client Native desktop SQL workbench Elyra SQL Anywhere Replication-ready SQL engine Elyra Sjá SEO & GEO workspace for macOS Elyra DataGrid Server-driven data grid for Laravel
Release notes
Changelog
Elyra

Browser

A bot can get its own browser for websites it is to work in, for example an accounting system or a CRM. The browser runs in the daemon, without a window. In the app you can watch it live and take over the mouse and keyboard.

The sites the bot may open

A bot has a browser only when it has sites. They are set in the bot's settings, Sites the bot may open in its own browser (with subdomains), or from the terminal:

huskarld browser-domains <bot> tripletex.no,hubspot.com

The field is read the way people write:

  • Sites are separated by commas, semicolons, spaces or line breaks.
  • An address is cut down to the host name: https://www.Tripletex.no/login?x=1 becomes tripletex.no.
  • www. and *. are removed, and a domain always covers its subdomains too. tripletex.no lets in app.tripletex.no.
  • Names with æ, ø and å become punycode, the way the browser sees them: blåbær.no becomes xn--blbr-roah.no.
  • A name without a dot (localhost), with a port or with a user name is rejected with "«…» is not a website". A colon between two names gives "«…» is not a website. Separate the sites with commas."

Empty field: the bot has no browser and no browser tools.

Only http and https are opened, and about:blank. Every document request (navigation, redirect, frame) is stopped in the browser and let through only to the bot's sites, no matter who asked for it: the bot, a redirect, a script, or you while you have control.

Everything else a page fetches goes through the guard too. Chrome is started with a proxy of the bot's own as its only route out, loopback included, so images, scripts, fetch and XHR, beacons, WebSockets and what workers fetch reach only the bot's sites and their subdomains. A page on an allowed site cannot send what it has read to another host, in an address or otherwise. QUIC is off, WebRTC goes only through the proxy, Chrome makes no DNS lookups of its own, and its calls home (updates, sync, Safe Browsing) are turned off or refused.

A real site often loads from other hosts: a CDN, fonts, a captcha. Those are refused like the rest, and the page may look broken. The computer panel then says what the page tried to reach, Stopped: …, with Allow fonts.gstatic.com for each; allowing adds it to the bot's sites, at once, also for the browser already open. Allow only what the page needs and you trust: an allowed host can receive what the bot's pages send it. A host that is not a website (localhost) is shown but cannot be allowed.

Which browser

Huskarl uses the first of these that exists:

  1. /Applications/Google Chrome for Testing.app
  2. /Applications/Chromium.app
  3. /Applications/Google Chrome.app
  4. /Applications/Brave Browser.app
  5. /Applications/Microsoft Edge.app

HUSKARL_CHROME points to another. If none exists, the tools answer "found no Chrome or Chromium on this Mac".

The browser is started headless on first use, at 1280 × 800 and with the same user agent as the regular Chrome version (without "Headless"). Each bot has its own profile in ~/.huskarl/browsers/<bot>, so logins and cookies stay between runs and are never mixed with your own browser or another bot's. The profile lives outside the bot's own folder, so the agent's sandbox does not see it.

The browser closes after 10 minutes without use (HUSKARL_BROWSER_IDLE_SECONDS), but not while you are watching it or controlling it. The profile is kept.

The tools

Tool Class What
browser_open Read opens an address on one of the sites
browser_snapshot Read the page's text, and every link, button and field with a ref (e1, e2 …)
browser_read Read all the visible text
browser_back Read goes back
browser_screenshot Read a JPEG image of the page, 1280 × 800
browser_fill Draft types into a field (replaces what is there)
browser_type Draft types where the cursor is, after browser_click_at
browser_click depends on what it hits clicks a ref. Requires label
browser_click_at depends on what it hits clicks a point in the screenshot. Requires label
browser_request_login Draft asks you to log in yourself

Everything from the pages, text and screenshots, is outside text. Memories written afterwards are held back (see Memory).

Passwords: fields for passwords, one-time codes and card numbers are marked in the snapshot ("password field: the user fills it in"), the value is shown as ••••, and browser_fill and browser_type refuse to type into them.

Screenshot and click at a point are for pages the snapshot does not understand: maps, canvas and special components. A screenshot costs much more than a snapshot.

Which class a click gets

A click is classified by the element it hits, not by what the bot says it is. The text is compared word by word, in English and Norwegian:

Hits Class
payment words: pay, buy, order, purchase, checkout, transfer, subscribe, betal, kjøp, bestill, overfør, abonner Finance
ending words: delete, remove, cancel, unsubscribe, terminate, deactivate, empty, slett, fjern, si opp, avbestill, kanseller, tøm, deaktiver, avslutt abonnement, avslutt avtale, avslutt konto Delete and publish
a send word, or a form sent, on a page that takes a card or shows an amount with a currency (1 299,00 kr, € 40) Finance
send words: send, submit, publish, post, share, invite, connect, reply, confirm, approve, sign, complete, finish, register, sende, send inn, publiser, del, inviter, koble til, svar, bekreft, godkjenn, signer, lever, fullfør, registrer Send
a button that submits a form with POST Send
something Huskarl cannot see into: canvas, iframe, embedded app, video, an svg without text Send
everything else Change internally

Whole words: "Sendt" is not "send". "Clear" is not an ending word, since it clears searches. On a page about money, «Bekreft» is a payment until shown otherwise: single-page apps rarely send a form with POST, so the button's text and the page are the signals there are. If Huskarl cannot work out what the click hits, it counts as Send.

The approval card is titled "Click «» on " and shows the page, where the form is sent (method and address), the point for browser_click_at, and a warning when Huskarl cannot see what is there: "Huskarl cannot see what is here (an image, a frame or an embedded app). Look in the browser before you approve."

label is checked. The text the bot gives must match the element. If the page has changed, the bot gets "the element is now called «…», not «…»; the page has changed, take a new browser_snapshot". An approved click is run the same way, so it clicks only if the page still shows what you approved.

The ledger and the browser. A click on "Next" twice is two pages. Huskarl counts how many times the page has changed (open, fill, type, go back, internal clicks), and the count is part of the ledger key. A new "Send" on the same page, on the other hand, is a repeat and is not sent again.

The computer panel

If the bot has sites, the thread's top bar has a screen button, See the bot's browser. It opens a panel beside the thread with the page live, the title and the address. While the browser starts, it says "Opening the browser…", and then "Waiting for the picture…" until the first frame arrives.

Frames arrive only while the panel is open. Old frames are released when a new one is shown.

Take over

Take over gives you the mouse, wheel and keyboard in the page. As long as you have it:

  • the bot sees nothing and can do nothing there. The tools answer "The user is controlling the browser now. Wait until the user says so before you use it again."
  • the panel says "You're in control. The bot sees nothing and can't do anything here until you give it back."

Give back returns control to the bot. The same happens when you close the panel. Without control, it says "The bot is in control. Take over to click and type yourself, e.g. to sign in."

Showing the bot how

Show the bot how beside Take over gives you the browser and writes down what you do: "Recording what you do. Give back when you are done, and the bot gets the steps." When you give back, the thread says "🎥 You showed the bot 6 steps", and the bot gets them, numbered:

1. Started at https://app.tripletex.no/invoices
2. Clicked «Ny faktura»
3. Typed «Acme AS» into «Kunde»
4. Typed into «PIN»: a secret, which the user types themselves
5. Clicked «Lagre faktura»
6. Came to https://app.tripletex.no/invoices/1043

It says in a line or two what it understood, and proposes it as a skill (see below). It does it the same way when you ask, or as a routine if you say so ("do this every Monday").

  • A click is described by what it hits, as the bot's own clicks are: the element's text, and whether it is a link.
  • What you type is written down per field, with the field's name. In a password, one-time-code or card field it is not: the step says only that a secret was typed there.
  • Addresses are written down without their query and fragment, where a form sent with GET puts what was typed and sites put tokens.
  • The steps are what the bot is told; the labels in them come from the page.

Saved as a skill

What you showed it is otherwise gone when the conversation moves on. So after a demonstration the bot proposes it as a skill, with skills_propose: a short name, the steps as it would do them, how to check it worked, where to stop and ask you, and what to do if a step fails. That is an approval card, Save the skill «New invoice in Tripletex», with all of it written out; with the name of a skill it has, Replace the skill ….

Approved, it is saved and goes into the bot's instructions, under "Skills the user showed you", noting that the names of buttons and fields in the steps come from the pages: they say what to click, they are not instructions. The bot follows it when you ask ("make the invoice for Acme"), and a routine can name it.

A skill is part of the bot's instructions, so it is as strict as a change to them: the card always waits for you, whatever the rules say, and approving it earns no rule; it is refused in a run that has read mail, pages or files.

Settings → Skills lists them, each with its steps, Test in rehearsal and Delete. A test is one run that does the skill in rehearsal, whatever the bot's own setting: nothing leaves or changes, every act is only written down, and the thread lists what it would have done, then the bot says how far it got, whether the check would pass and where it would stop for you. Not while the bot is busy. In rehearsal a click that changes something is only written down, so a test goes as far as the first such step.

Logging in for the bot

If a site needs a login, the bot calls browser_request_login and stops. The thread gets the card Sign in to with the reason, the text "You sign in yourself, in the bot's browser. The bot sees neither the password nor the screen while you do." and two buttons:

  • Open the browser opens the panel and takes over for you.
  • Done, continue gives control back and sends the bot "I've signed in to . Go on."

You type the password into the page, from your own keyboard. The login lives in the bot's browser profile. Huskarl does not store it.

Take-over latency

scripts/latency/run.py measures the time from a keystroke being sent over IPC until a frame of the page shows it: the daemon, CDP, Chrome and the screencast. Measured on October 16, 2026:

Median p90 Max
Keystroke to frame 12 ms 14 ms 30 ms

The target was under 150 ms. The app's decoding and drawing come on top, about one screen refresh.

Not yet

  • Chrome for Testing shipped with the app, in a fixed version that does not update itself in the middle of a run.
  • Signing in yourself, tried against real services.
  • A list of hosts a site's pages may load from, apart from the sites the bot may open: allowing a CDN today also lets the bot open it.