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 needs Screen Recording granted 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.
Until then, System Settings goes on showing Arin switched on after an upgrade, because the switch belongs to the build before. When permissions go wrong has the command that moves it.
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.
Upgrading from 0.6.1 or earlier
Arin's bundle identifier changed from com.anistark.arin to dev.anirudha.arin after
0.6.1. macOS files the Screen Recording grant under that identifier, so it sees the new
build as a different app: grant Screen Recording once more. The old row stays in System
Settings until you clear it:
tccutil reset ScreenCapture com.anistark.arin
A login agent an earlier build installed keeps working, because it names the same app.
arin service status points it out, and arin service enable replaces it with one under
the new identifier.
When permissions go wrong
If Screen Recording reads as missing after you have granted it, start with the daemon's log, which says on every start what it found:
tail ~/Library/Logs/Arin/arin.log
Then:
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. The log names the first, and arin permissions names the second.
The grant belongs to another build. The usual one. Every brew upgrade leaves it
behind, and so does a development build from just bundle sitting beside an installed one.
macOS keeps a single row for Arin and pins it to the exact build it was granted to, so the
switch shows on while every other build is refused. The log says so and names the build the
grant belongs to. Clearing the row is what moves it:
tccutil reset ScreenCapture dev.anirudha.arin
Then, from the build you mean to run, choose Screen Recording from Arin's menu bar item,
which puts Arin back in the list, switch it on, and restart Arin, with arin service restart for the login agent. Arin asks macOS once for each build rather than at every
start, because in this state macOS would put its dialog up every time. That is why the menu
bar item is the way back in.
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 dev.anirudha.arin
Then start Arin and grant it once more. A grant made this way holds until that build is
replaced, so it has to be given again after every upgrade until releases are signed with a
certificate. 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 "$HOME/Library/Application Support/Arin"
tccutil reset ScreenCapture dev.anirudha.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/dev.anirudha.arin
rm -f ~/Library/LaunchAgents/dev.anirudha.arin.plist
An agent installed by 0.6.1 or earlier is under com.anistark.arin instead, so name that
in both lines.
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.