Install

macOS 14 or newer, on Apple silicon or Intel. There is no Linux or Windows build: the renderers for both are v2, so the daemon would speak the whole protocol and draw nothing.

Four ways in. Homebrew is the one to use unless you have a reason.

Homebrew

brew install anistark/tools/arin

That is a tap, anistark/tools, and the formula builds Arin from source on your machine. It needs no Rust toolchain of your own: Homebrew installs one for the build and cleans up after itself.

Building rather than downloading is the point, not a shortcut. There is no certificate yet, and macOS quarantines anything unsigned that arrived over the network, so a downloaded app gets refused by Gatekeeper. Something compiled where it runs was never downloaded, carries no quarantine attribute, and Gatekeeper never engages. A certificate lands in 0.7, and the formula is replaced by a cask then.

The bundle is still ad-hoc signed, which buys nothing from Gatekeeper and is not optional anyway: macOS will not remember a Screen Recording grant for a bundle whose signature does not verify.

It installs Arin.app into the Homebrew prefix and links the binary inside it onto your PATH, so arin and the app are one file rather than two that could drift.

brew upgrade arin       # a newer version
brew uninstall arin     # and gone

Two consequences of the app living in the Homebrew prefix rather than /Applications: Spotlight will not find it, and every upgrade asks for Screen Recording again. Both are things a certificate fixes. Without one, the grant is pinned to the exact binary it was made against, so replacing that binary voids it. A build signed with a Developer ID satisfies the same requirement as the one before it, which is what lets a grant outlive an upgrade.

Nix

nix run github:anistark/arin -- -d

Also builds from source, also needs nothing installed beforehand, and additionally gives you a nix-darwin module for starting at login with the daemon's options under version control. Nix is the whole story, including services.arin and its options.

The dmg

Every release attaches one, and the checksum is in the release notes.

It is unsigned. macOS will refuse it the first time and you will have to allow it in System Settings under Privacy and Security. If that is not something you want to do, use Homebrew, which avoids the question rather than answering it. This is the one install route that gets easier rather than harder in 0.7.

From source

git clone https://github.com/anistark/arin && cd arin
cargo install --path crates/arin-cli

Or without cloning:

cargo install --git https://github.com/anistark/arin arin-cli

Needs a Rust toolchain. This installs the bare binary and not the app bundle, which is fine for driving the daemon and worse for living with: the menu bar item, the absent Dock icon, and a Screen Recording grant that survives a rebuild are all properties of the bundle. just bundle builds Arin.app from a clone if you want both.

cargo install arin does not work, on purpose. arin is a library on crates.io, so Cargo correctly answers that there is nothing to install. The binary is arin-cli.

First run

arin -d

macOS asks for Screen Recording. Arin needs it for two things: noticing when content moves under a mark, and picking a colour that can be seen against whatever is underneath. Those frames are compared in memory. They are not written anywhere and not sent anywhere.

It is the only permission Arin asks for. In particular it never asks for Accessibility, which is the one that would let it act on your behalf, and it never will: Arin draws and never clicks.

arin -d runs in the foreground and stops on Ctrl-C. That is deliberate. Backgrounding is launchd's job, below.

Starting at login

Not automatic, either way. An annotation daemon that added itself to your login items unasked would be doing the thing people reasonably object to.

arin service enable

The same line however Arin got here. It works out which Arin.app to run from the binary you typed it with, so there is no path to get right and no way to point the agent at a different build of Arin than the one you meant. Re-running it replaces the agent, which is how you point it at an app that moved.

arin service status      # installed? running? which build?
arin service restart     # what to run after brew upgrade
arin service disable     # stop starting at login, leave the app alone

status exits non-zero when the agent is not installed, so a setup script can ask.

The agent names a path that survives an upgrade, so brew upgrade arin leaves it working. What an upgrade does not do is replace a daemon that is already running, so the old build stays up until arin service restart says otherwise.

If you have a bare binary from cargo install, there is no bundle for the agent to start and Arin will say so rather than installing one. Screen Recording is granted to a bundle rather than to a path, so an agent running a bare binary comes up unable to see the screen. Build a bundle with just bundle and name it:

arin service enable --app target/bundle/Arin.app

With Nix, it is services.arin.enable = true in your nix-darwin configuration, which is the same launch agent with the daemon's command line written down. See Nix. arin service knows about it and refuses to manage an agent nix-darwin is managing, rather than overwriting it and leaving two definitions of one agent.

When permissions go wrong

If the daemon keeps asking for Screen Recording after you have granted it, start here:

arin permissions

It reports the permission and, when something is wrong, the thing the permission cannot tell you: whether this build has an identity a grant can attach to at all. macOS remembers a grant against a code signature rather than against a name, so a build whose signature does not verify reports exactly what a build nobody has granted reports. Only one of those is fixed in System Settings, and switching the row on for the other does nothing however many times you do it.

Two causes, and the command above tells them apart.

The build is not signed properly. Arin installed before this was fixed carries a signature that does not verify. Re-signing it is enough, and any ad-hoc signature will do:

codesign --force --sign - /opt/homebrew/opt/arin/Arin.app
tccutil reset ScreenCapture com.anistark.arin

Then start Arin and grant it once more. A grant made this way holds until that build is replaced, so an upgrade will ask again until releases are signed with a certificate.

Two builds are competing for one row. macOS identifies unsigned code per binary and shows a single row for the identifier, so toggling it updates whichever record it reaches and the other keeps asking. Reset the same way, then start the one you actually meant to run. arin diagnose reports which build you are talking to.

Uninstalling

If you enabled the login agent, disable it first. The command that does so is the app, so removing the app first takes it with it and leaves an agent behind pointing at a binary that is no longer there.

arin service disable

Then the app:

brew uninstall arin                  # Homebrew
nix profile remove arin              # Nix
rm -rf /Applications/Arin.app        # the dmg

None of those remove what Arin left in your home directory, because none of them should guess. If you want it gone completely:

rm -rf ~/Library/Logs/Arin
tccutil reset ScreenCapture com.anistark.arin

If the app went first, there is no arin left to run and the two lines it would have run are:

launchctl bootout gui/$UID/com.anistark.arin
rm -f ~/Library/LaunchAgents/com.anistark.arin.plist

The socket lives in your temporary directory and is recreated on every start, so there is nothing to clean up there. There are no accounts, no config in ~/.config, and no telemetry, so there is nothing else.