A small macOS browser built on Chromium, with a native Swift/AppKit interface and a built-in agent panel that can drive the browser.
Website: sorrycc.github.io/Tiller
tiller-promo-music.mp4
- Chromium engine, native shell. Pages render with Chromium through CEF; the window, tabs and menus are AppKit.
- Tabs and session restore. Tabs along the top or in a sidebar, with a context menu; open tabs and recently closed tabs survive restarts and crashes, and the start page brings the closed ones back.
- The usual browser things. Find in page, zoom, print, a link's address under the mouse, pages going full screen, Chromium's developer tools and view source.
- Downloads. Files go to
~/Downloads, with progress in the toolbar. - Import from Chrome. Cookies, saved passwords, history, extensions, search engine and homepage.
- Chrome extensions. Add unpacked folders or CRX files, or bring Chrome's over. Content scripts, background workers and popups run; Chrome's tab and window APIs don't see Tiller's tabs.
- Profiles. Each profile has its own site data, history, passwords, settings and chats, and runs as its own app instance.
- Agent panel. Chat with Qoder CLI, Claude Code, Codex, Antigravity CLI or Grok Build in a side panel that controls the browser, and call skills with
/, from your own folders or a skill library each agent shares. Prompts can also run on a schedule, skills included, set up in Settings or by asking the agent. - Browser tools. A stdio MCP server and the
tillercommand-line tool expose the same tools to external agents and scripts.
- macOS with the Swift toolchain (Xcode Command Line Tools)
- Rust, installed through rustup
- Ninja
- CEF 154, matching the
cefversion pinned inCargo.toml
Install the toolchain and download CEF once:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
brew install ninja
git clone https://github.com/tauri-apps/cef-rs && cd cef-rs
git checkout cef-v154.2.0+154.0.28
cargo run -p export-cef-dir -- --force $HOME/.local/share/cefThe build reads CEF from $CEF_PATH, which defaults to ~/.local/share/cef. Update the download whenever the pinned cef version changes.
That download can't play H.264 video or AAC audio, which X, most news sites and most MP4 files use, because the stock CEF builds leave those patent-encumbered codecs out. To play them, put a CEF framework built with proprietary_codecs=true in ~/.local/share/cef-codecs. The build uses the framework there when present, and the headers from $CEF_PATH either way. aiexkwan/aurix-cef publishes such a build for the same Chromium as the pinned CEF, made by a third party and unsigned, so check that it's one you're willing to run:
brew install zstd
mkdir -p ~/.local/share/cef-codecs
curl -L https://github.com/aiexkwan/aurix-cef/releases/download/cef-154.0.26-codecs/cef-154.0.26-macosarm64-slim.tar.zst \
| tar --use-compress-program=unzstd -x -C ~/.local/share/cef-codecsOr build CEF yourself with proprietary_codecs=true ffmpeg_branding=Chrome and copy its framework there. Set CEF_FRAMEWORK_DIR to use a framework from another folder. The framework's version must share the pinned CEF's major version and API hash; the script prints the one it picked.
scripts/bundle.sh # release build; pass `debug` for a debug build
open build/Tiller.app
open build/Tiller.app --args -url https://example.com # open a page at launchThe script builds the Rust crates and the Swift app, then assembles an ad-hoc signed build/Tiller.app, which doesn't update itself. To keep the bundle small, it includes only the English and Chinese Chromium locales and omits SwiftShader, so there is no software rendering fallback when the GPU is unavailable.
Releases are signed with a Developer ID, notarized, and published on GitHub as Tiller-<version>.dmg for installing and Tiller-<version>.zip for Sparkle, which updates installed copies from the appcast at https://sorrycc.github.io/Tiller/appcast.xml. A version with a pre-release part, such as 0.2.0-beta.1, is a beta: a GitHub pre-release that only Tillers with Settings > General > Include beta versions turned on update to.
To release, set version in Cargo.toml, commit, and either push the tag v<version> to have the Release workflow do it, or run scripts/release.sh on a Mac set up as below. scripts/fetch-cef.sh downloads CEF and the codecs build for the workflow, checking the codecs archive against a pinned hash.
One-time setup:
-
In the Apple Developer site, create a Developer ID Application certificate and install it in your login keychain. Export it from Keychain Access as a
.p12with a password. -
At account.apple.com, create an app-specific password for notarization.
-
Make Sparkle's update signing key with
app/.build/artifacts/sparkle/Sparkle/bin/generate_keys(after a build), which keeps it in your keychain and prints the public key. Put that inscripts/sparkle-public-keyand commit it.generate_keys -x sparkle-private-keyexports the private key for the workflow; delete the file afterwards. -
In the repository's Settings > Secrets and variables > Actions, add:
Secret Value MACOS_CERTIFICATE_P12base64 -i certificate.p12MACOS_CERTIFICATE_PASSWORDthe .p12's passwordAPPLE_IDyour Apple Account email APPLE_APP_SPECIFIC_PASSWORDthe app-specific password APPLE_TEAM_IDyour team ID SPARKLE_PRIVATE_KEYthe exported private key -
After the first release creates the
gh-pagesbranch, turn on GitHub Pages for it in Settings > Pages. -
To release from your Mac too, store notarization credentials once:
xcrun notarytool store-credentials tiller-notary --apple-id <email> --team-id <team> --password <app-specific password>.
Agents in the panel use Tiller's browser tools automatically. External agents and scripts can use the MCP server at build/Tiller.app/Contents/MacOS/tiller_mcp, or the tiller command-line tool, installed from Tiller > Install Command Line Tool…:
tiller new example.com # open a tab and wait for the load
tiller read # page text with numbered links, buttons and fields
tiller click 3
tiller type 5 "hello" --submitSee Browser tools for every tool, command and option.
| Path | Description |
|---|---|
core/ |
Rust static library linked into the app. Loads CEF and owns the browsers. |
helper/ |
Rust binary for the CEF subprocesses. |
mcp/ |
Rust crate with the browser tools, the tiller_mcp MCP server and the tiller command-line tool. |
app/ |
SwiftPM package with the AppKit app. No Xcode project is required. |
scripts/bundle.sh |
Builds everything and assembles build/Tiller.app. Signs it with TILLER_SIGN_IDENTITY when set. |
scripts/release.sh |
Signs, notarizes and publishes a release, and adds it to the appcast. |
| Guide | Covers |
|---|---|
| Using the browser | Tabs, find and zoom, address bar, history, saved passwords, Chrome import, profiles |
| Agent panel | Chats, image attachments, skills, scheduled prompts, agent permissions, Codex isolation |
| Browser tools | MCP server, command-line tool, debug launch arguments |
| Settings and data | Settings, Chromium switches, data folder, environment variables, migrations |
