Marlin
Open source terminal · MIT

The terminal where you read
what your agents did.

You do not run one command and watch it any more. You run four agents in four panes, and the real work is reading what they changed. That part happens here, without leaving the terminal.

  1. They run. Four agents, four panes, a dot on each one that only appears when it knows something you do not: still running, finished, failed.
  2. You read. Every repository in the tree shows its branch and how many files it has touched. Click the number and that repository's changed files become a tab of the sidebar.
  3. You decide. Click a file for its diff, unified or side by side, stage or discard the row, and commit what you accepted. Escape puts your panes back exactly as they were.
See it work Install Source
Marlin
Marlin
>
bar top panes 1 theme Marlin Dark a mock, not the app

A working mock, running in this page. Nest a couple of splits, rotate the tab bar, click a file to watch it take over the tab, right-click a pane, double-click a tab to rename it. It behaves the way the real app behaves.

What it does

All of this ships today
Splits

A tab holds a tree

Vertical and horizontal splits nest, so any mix is just what happens when you press the second key. Closing a pane collapses its parent and gives the space back to its sibling.

Tabs

Three-state bar

Top, side, or hidden, cycled with one key. Long branch names fit down the side of a wide display where they never fit across the top of one.

Git

Staging, conflicts, diffs

Branch, ahead count, merge conflicts, staged and changed, with stage, unstage and discard on each row. Diffs render unified or side by side. It runs your git.

Review

Every repo shows its branch and its count

A folder of repositories is what a working directory usually is, so each one in the tree says which branch it is on and how many files it has touched. Click the number and its changed files become a tab of the sidebar: read a diff, stage it, commit what you accepted.

Branches

Switch without leaving the tree

Click a branch to filter, switch, create, or check out a remote one as a local branch that tracks it. Click any branch to see what it has that yours does not, file by file. Merging and pushing stay in the pane below, where their output can be read.

Reading

Files take over the tab

Click a file and you get three columns: tree, file, terminal. Escape puts your exact layout back. Syntax highlighting across 30 languages, and files are editable.

Agents

Status you can see

A dot per pane and per tab, driven by real shell marks rather than guesswork: running, finished, failed. A dot appears only when a pane knows something you do not.

Notifications

Only when you looked away

A long command that finished in a pane you were not watching. Notifying about anything already on your screen is how a notification becomes noise, and noise gets muted.

Updates

The button that tells you installs it

Settings checks a static file, and the same button downloads the disk image, replaces the app and relaunches. One GET, no identifiers, and it is listed in the table below.

Layout

Panes move, and come back

Drag a pane to an edge to move it there, to the middle to swap, to a tab to send it. Your tabs, splits and directories are restored on the next launch, and a moved pane keeps the shell it was already running.

Care

Nothing closes silently

⌘W names what it is about to close, the pane, the tab, or Marlin, and lists what is still running in it. Return answers the focused button, which is Cancel. ⌘+ and ⌘- scale the whole interface, terminals included, and the level is a line in your config file.

Editing

Click a file and type

Files open ready to edit, highlighted while you type, with a gutter and ⌘S. A write is refused if the file changed on disk since you opened it.

The whole key map

⌘/ shows this inside the app
⌘D · ⌘⇧DSplit vertically, split horizontally
⌘WClose the pane, or the tab when it was its last, asking first
⌘T · ⌘1–9New tab, go to tab
⌃⇥ · ⌘] · ⌘[Cycle tabs
⌘⇧AFocus the next pane that wants you: running, waiting or failed
⌘⇧P · ⌘P · ⌘⇧FCommand palette, go to file, search every file
⌘B · ⌘⇧BToggle the sidebar, cycle the tab bar
F2 · ⇧F2Rename the pane, rename the tab
⌘⇧↩Zoom one pane to the whole tab
⌘F · ⌘KFind in scrollback, clear the buffer
⌘+ · ⌘- · ⌘0Scale the whole interface, and back to 100%
⌘E · ⌘S · EscEdit the open file, save it, put your panes back
⌘, · ⌘/Settings, this table

Why not the terminal you already have

The honest version

Every one of these is a good program, and three of them are faster at being a terminal than Marlin is today. The question is what happens after the agent stops printing.

Instead ofWhat you would miss hereWhat you would give up
iTerm2 Reading the change: git status, diffs and staging in the window, and a per-pane dot that says which agent needs you. Fifteen years of features. Profiles, triggers, tmux integration, shell autocomplete: Marlin has none of them.
Ghostty or Alacritty The same. They are terminal emulators, deliberately, and the review half of the loop happens in another window. Speed that has been measured. Marlin's renderer is unmeasured and this page will not claim otherwise.
Warp Nothing you would notice at first; Warp is the closest thing to this idea. An account, a cloud, AI in the loop and closed source. Marlin has no backend at all, and the trade runs the other way: no team features, no shared blocks.
The terminal in your editor Panes that are a first-class layout rather than a drawer, and a review view that does not fight the editor for the window. Your extensions, your language servers, your debugger. Marlin's editor is a textarea and says so.

Security, and what leaves your machine

One request, and it is listed

A terminal sees everything: your keys, your tokens, your employer's source. That makes the interesting question not what a terminal can do, but what it refuses to do. Marlin's answers are deliberate, and they are checkable, because the source is right there.

PromiseWhat that actually means
No cloud, no account, no sign-inMarlin has no backend. There is nothing to log into, and no service that could be down or shut off.
No telemetry, of any kindNot opt-out, not anonymised, not “just crash counts”. There is no analytics code in the app.
Your terminal output is never stored or sentScrollback lives in memory in your session, and the diagnostics log deliberately contains none of it, because scrollback holds passwords and private source.
Error reports are a button, not a background jobProblems are written to a local file. Reporting opens a GitHub issue pre-filled and editable, so you read exactly what you are sending before you send it.
No AI in the loopMarlin does not read your commands, does not send them anywhere, and has no model in it. It runs agents; it is not one.
Your shell stays your shellMarlin never parses your command line. Completion, history and autosuggestions remain fish’s and zsh’s job, so your history file is never duplicated somewhere new.
Your git is your gitThe sidebar shells out to the git binary you already have, with your config, your credentials and your hooks. No second implementation with its own idea of what your repository contains.
The webview is locked downIt can receive terminal output and move its own window. No filesystem, shell or network permission, and a content security policy that blocks outbound requests, so nothing the terminal prints can call home.
Links open in your browser, and only http and httpsA UI that can be navigated by something the terminal printed is a class of problem worth closing before it exists.
Settings are a file you own~/.config/marlin/marlin.toml, plain text, editable by hand, safe to keep in a dotfiles repo.
The update check is the only thing that goes outAt most one GET a day to a static version.json, carrying no identifiers and no version query string, plus the disk image itself if you ask for it. Those are the only outbound requests Marlin makes, and there is no signature to verify on what comes back: TLS to the two hosts is the whole of what it trusts.

One honest caveat. Builds are not yet signed with an Apple Developer ID, so macOS will warn you about an unidentified developer. The instructions below say how to get past it. The 0.1.1 disk image on the releases page today is a worse case than that, and needs one extra command before macOS will let you make that decision at all; the download section explains it. Building from source avoids the question entirely, and is the option we would pick if we were you.

Not done yet

Kept honest in both directions

Eleven items that used to be on this list have been built and were taken off it, most recently session restore, panes you can move, and the measurement harness that rewrote the first two lines above. A stale gap list flatters you exactly as much as no gap list at all.

Install

macOS, Windows and Linux

What exists to download today is one file: an aarch64 disk image on the releases page, version 0.1.1, built by hand on one Apple silicon Mac. If you are on an Intel Mac, on Windows, or on Linux, the honest answer right now is to build it from source, which takes one command and is written out below.

You are onTodayIn 0.1.2
macOS, Apple silicon The aarch64 disk image on the releases page. One universal image, both architectures in the same file.
macOS, Intel Build from source. The same universal image.
Windows 10 or 11, x64 Build from source. A -setup.exe that installs for the current user with no admin prompt, and an .msi for anyone deploying it.
Linux, x64 Build from source. A .deb for Debian and Ubuntu, and an .AppImage for everything else. Neither is signed and neither needs to be: nothing on Linux asks. The AppImage is around 78MB against a 6MB disk image, because it carries the webview with it.

Nothing is signed by a certificate authority. There is no Apple Developer ID and no Windows signing certificate, so macOS and Windows both stop you the first time. That warning is accurate: nobody has paid anyone to vouch for this build. It happens once. Once you have allowed it, macOS saves the exception and Marlin opens by double-clicking from then on, and updates install in place without ever asking again.

Read this before you download the 0.1.1 disk image, because the paragraph after it does not apply to that file. The one artefact on the releases page today, Marlin_0.1.1_aarch64.dmg, was built by hand on 16 August before the signing bug below was found. It is not merely unsigned, it is malformed: the binary carries the linker's ad-hoc signature, which seals no bundle resources, and macOS reads a signature that promises resources it cannot find as corruption. So you do not get the unidentified-developer warning. You get “Marlin is damaged and can’t be opened. You should move it to the Trash”, whose two buttons are Done and Move to Trash. The Open Anyway route described further down does not apply to it: that exists for the unidentified-developer rejection, and this bundle is refused one step earlier, before trust is the question.

Two things that are widely recommended for this dialog do not work here, and we measured that rather than assuming it. All three rows below are spctl -a -vvv -t exec against the published 0.1.1 bundle, run 29 August 2026. Exit 1 is a malformed bundle. Exit 3 is the ordinary unidentified developer, which is the one you can click through.

The disk image as downloadedexit 1 · code has no resources but signature indicates they must be present
After xattr -dr com.apple.quarantineexit 1 · unchanged. The quarantine flag was never the problem, so removing it changes nothing.
After codesign --force --deep --sign - /Applications/Marlin.appexit 3 · rejected. Now it is the normal unidentified-developer case, and Open Anyway appears.

So if you want to run 0.1.1 today: drag it to Applications, run codesign --force --deep --sign - /Applications/Marlin.app, then follow the macOS paragraph below as normal. That re-seals the bundle on your own machine with the same ad-hoc signature the build should have carried. It vouches for nothing and it is not a substitute for notarisation; it only turns an unopenable file into one you can consciously decide to trust. If you would rather not run that command on a stranger’s binary, and that is a reasonable position, build from source or wait for 0.1.2.

Fixed at source on 22 August, in the build rather than in this advice. "signingIdentity": "-" in tauri.conf.json makes tauri-bundler actually run codesign instead of skipping the step, and a release job now fails the build if Sealed Resources ever goes missing again. Neither has reached a download yet, because no tag has been through the pipeline since. 0.1.2 is the first build that will carry it.

On macOS, from 0.1.2 onwards, you get a dialog headed “Marlin” Not Opened, saying Apple could not verify Marlin is free of malware. Its two buttons are Done and Move to Trash, and Move to Trash is the highlighted one, so do not press it out of habit. Open System Settings, then Privacy & Security, scroll to Security, and click Open Anyway next to Marlin. That button is only there for about an hour after you tried to open it. Right-clicking and choosing Open used to do the same job and stopped working in macOS Sequoia. If you would rather stay in a terminal, xattr -dr com.apple.quarantine /Applications/Marlin.app does it, and downloading with curl rather than a browser skips the question entirely, because the flag that triggers it is attached by whatever downloaded the file. All of that is about a well-formed unsigned bundle, which is what 0.1.2 will be and what 0.1.1 is not.

On Windows, SmartScreen shows “Windows protected your PC”. Choose More info, then Run anyway. On Linux there is none of this: nothing asks for a signature and neither package raises a warning of any kind.

Take the .deb if apt is an option, which is why it is listed first. It declares its dependencies, so apt resolves them for you. The AppImage carries the webview but not the whole desktop, and CI has twice caught it wanting a library a bare system did not have, libfontconfig.so.1 and then libfribidi.so.0. Both are ordinary parts of a GTK desktop and you almost certainly have them, but nobody has yet run either package on a real Linux desktop, so that is not a claim being made here. If the AppImage exits complaining about a missing shared library, take the .deb.

What the AppImage does not need is FUSE 2, whatever the usual AppImage advice says. Its runtime is static-pie linked, which file and ldd both confirm on every build, so it cannot be missing libfuse.so.2 and installing libfuse2 is not a step you need. --appimage-extract-and-run works if you would rather not mount anything at all. This paragraph said the opposite until 23 August 2026. It described classic AppImage behaviour that this AppImage does not have, and it was written a few hours before the smoke test existed to check it.

The Windows build is a pipeline, not yet a fact. The release workflow that produces those installers, and that installs the Windows one on a runner to check it starts, has run and gone green on all three platforms, most recently on 22 August 2026. What it has never done is run against a tag, which is what version 0.1.2 will be. And that check proves the installer works and the process starts, not that the window renders or that typing does: nobody has sat in front of Marlin on Windows, and nobody has run the Linux packages at all. Say so in an issue if it misbehaves; it is a bug, not a known limitation.

From source

$ git clone https://github.com/ehsangazar/marlin
$ cd marlin
$ pnpm install
$ pnpm tauri build

Needs Node, pnpm and a stable Rust toolchain. The app lands in src-tauri/target/release/bundle, and building it yourself avoids the signing warnings entirely.

Shell integration

# zsh, from the installed app
$ echo 'source "/Applications/Marlin.app/Contents\
/Resources/shell/marlin.zsh"' >> ~/.zshrc

# or from a clone, run in the repository
$ echo "source $PWD/shell/marlin.zsh" >> ~/.zshrc

Optional, and the hooks ship inside the app so there is nothing to download. It emits standard prompt marks so the status dots and directory tracking work. Without it you get a working terminal with no dots, which is degraded rather than broken.

Something broken?

Issues get picked up

Open an issue with what you did and what happened. Agents triage every issue, reproduce what they can, and open a pull request where the fix is obvious. A human reviews everything before it ships, so nothing lands unread.

Report a bug Request a feature Sponsor

Marlin is free and always will be. Sponsorship is monthly and cancellable on purpose, because this is an early project and you should be able to stop as easily as you started.