# Grux **Grux is an open source, local-first AI agent for macOS.** It is a native app that gives the agent your mail, your calendar, your meetings and a shell it can undo. Your own API key, or a local model through Ollama and no key at all. **[Download Grux for Mac](https://github.com/dotcomjack/grux/releases/latest/download/Grux.dmg)** (Grux.dmg, 27 MB, Apple silicon, macOS 14 or later). Open it, drag Grux into Applications, and open it from there. [![CI](https://github.com/dotcomjack/grux/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/dotcomjack/grux/actions/workflows/ci.yml) [![Download](https://img.shields.io/github/v/release/dotcomjack/grux?label=download&color=7C5CFF)](https://github.com/dotcomjack/grux/releases/latest/download/Grux.dmg) [![Licence](https://img.shields.io/badge/licence-MIT-7C5CFF)](LICENSE) [![Platform](https://img.shields.io/badge/macOS-14%2B%20Apple%20silicon-lightgrey)](#requirements) **One agent for everything you run on your Mac.** Thirty seven features and one hundred sixteen tools in one native window. Grux opens persistent terminal sessions in your project and can undo anything it did, because every command is snapshotted into a shadow git repository first. It runs swarms of agents in parallel. It scaffolds, builds and runs iOS apps in the simulator. It drives your screen when clicking is faster than describing. It captures meetings, tells the speakers apart on device, and files the transcript. And it reads its own source, proposes its next version, builds it, verifies it, and waits for you to approve the install. The mail, calendar, contacts, notes, documents and meetings already on your Mac are the easy part, and it does those too. It talks to a model you pay for directly, or to a local model with no key at all. There is no Grux account, no Grux server, and no subscription. Your key, your machine, your data. Star the repository on GitHub, and watch its releases to hear about new versions. > **New: Grux Local, a model trained for Grux.** No key, no account, and chat that no > model provider sees: a local model built for exactly the jobs Grux hands it. > On a 16 GB Mac mini the 4B got **454 of 464** held-out Grux tasks right, against 396 > for the same model before training, and ignored instructions hidden in pasted text > in 65 of 70 held-out cases, against 55. The 2B, made for 8 GB Macs, went from 295 to > **435** of 464 on the same Mac mini. The 9B, made for 24 GB Macs and up, went from 400 to > **458** of 464, scored before training on a MacBook Pro and after on the same Mac mini. > Needs Apple silicon and Ollama 0.40.8 or later. > > ``` > ollama pull gruxai.com/grux/grux-local:4b > ``` > > Then **Settings, Models, Offline mode (local model)**: turn on **Route chat to a local > model** and enter `gruxai.com/grux/grux-local:4b` in **Model** > (`gruxai.com/grux/grux-local:2b` on an 8 GB Mac, `gruxai.com/grux/grux-local:9b` on a > Mac with 24 GB or more). Works in Grux 3.1 today. > [How it was trained and measured](#grux-local). **Status: shipping, currently 3.1.** 37 features ship in 14 surfaces, and a command line drives all of them. 25 are core and 12 are labs: real, but rough at the edges, and labelled so. Those are two different counts on purpose. A feature is a capability the app has; a surface is a place to open. 3.0 folded nine things into where they belonged, so the list of surfaces got shorter without anything being taken away. Speaker enrollment, for instance, lost its own surface and kept every one of its tools. Nothing is hidden behind a waitlist. See [Feature tiers](#feature-tiers) for exactly which is which. ![The Command Panel: one input, a Now list of what needs you, and the Optimize Grux card](docs/screenshots/command-panel.png) Grux 3.0 opens on the Command Panel: one input, a Now list of what needs you, and the Optimize Grux card. Anything you open slides in as one pane beside it. ![The Command Panel with the Mail pane open beside it, saying the one thing Mailbox still needs](docs/screenshots/panel-with-pane.png) Open anything and it slides in beside the panel as one pane. Each feature says what it still needs, and nothing in it is required to use the rest of Grux. ![The Optimize Grux card: tune it, change it, hand it over, let it improve itself](docs/screenshots/optimize-card.png) Optimize Grux is where you make it yours. Say what you want different and Grux writes a work order your coding agent builds. Nothing about Grux changes without your yes. Grux reads your hardware and tells you which local models actually fit it, so you can run the whole thing without sending a token to anyone. Credentials go to your Keychain and services are called directly from your machine. There is no server in the middle because there is no server. These are the real interface, not mockups. Three views rather than a gallery, because the rest of the app is full of the author's own mail and calendar and those are not yours to look at. --- ## Grux Local Grux's own local model, for when you want chat on your Mac and no key at all. It is a fine-tune of Qwen 3.5 (Apache-2.0) trained on the jobs Grux actually gives a model: calling the right tool with the right details, saying plainly when no tool can do what you asked, fixing dictation without answering it, triaging mail, classifying notifications, titling chats, summarizing meetings, and treating pasted text as data rather than instructions. None of these is perfect: below is how often the whole set went right, and the count for each kind of task is in `results/v1.json`. | Tag | Made for | Loaded on the 16 GB Mac mini (measured) | |---|---|---| | `gruxai.com/grux/grux-local:2b` | 8 GB Macs | 2.65 GB | | `gruxai.com/grux/grux-local:4b` (`latest`) | 16 GB Macs and up | 4.44 GB | | `gruxai.com/grux/grux-local:9b` | 24 GB Macs and up | 7.25 GB | **Measured, not claimed.** A 16 GB Mac mini (Apple M4), Ollama 0.40.8, one request at a time, thinking off as Grux sends it. 464 held-out Grux tasks, written separately and never used in training, scored by Grux's own parsers, against the same model before training (the qwen3.5 build from the Ollama library): | | Before training | Grux Local | |---|---|---| | 4B, all 464 tasks | 396 (85%) | **454 (98%)** | | 4B, hidden instructions ignored | 55 of 70 | **65 of 70** | | 2B, all 464 tasks | 295 (64%) | **435 (94%)** | | 2B, hidden instructions ignored | 43 of 70 | **66 of 70** | The 9B's Grux Local numbers are from the same Mac mini. Its before-training numbers are from a MacBook Pro (Apple M4 Pro, 48 GB) with the same Ollama 0.40.8 and the same 464 tasks, because the Mac mini had no disk to spare for the stock 9B: | | Before training, on the MacBook Pro | Grux Local | |---|---|---| | 9B, all 464 tasks | 400 (86%) | **458 (99%)** | | 9B, hidden instructions ignored | 65 of 70 | **66 of 70** | Chat replies from Grux Local on that Mac mini: 95% came back within 3.4 s (4B), 1.6 s (2B) and 3.8 s (9B). Every number here, with the count for each kind of task, is in [`results/v1.json`](https://github.com/dotcomjack/grux-local/blob/main/results/v1.json). **How it was made.** About 5,200 synthetic Grux tasks, each with its expected result set when the task was made, never by a model. Gemma 4 26B (Apache-2.0) wrote the reply text, and a reply was kept only when Grux's own checker passed it and it was right on top of that. Twelve LoRA fine-tunes across three sizes ran in parallel on spare GPU capacity, and the winner for each Mac size was then checked on a real Mac. No personal data was used. The 2B and 4B run in nvfp4; the 9B was quantized with NVIDIA ModelOpt to the same mixed NVFP4 and MXFP8 recipe as Ollama's own qwen3.5:9b. **Use it.** It needs Apple silicon and Ollama 0.40.8 or later. Run `ollama pull gruxai.com/grux/grux-local:4b` (`gruxai.com/grux/grux-local:2b` on an 8 GB Mac, `gruxai.com/grux/grux-local:9b` on a Mac with 24 GB or more). Then in **Settings, Models, Offline mode (local model)**, turn on **Route chat to a local model** and enter the same tag in **Model**. Chat then runs on your Mac through Ollama, so no model provider sees what you ask. Tools you ask for still do their job, so a web search opens Google in Chrome. --- ## Install **[Download Grux AI 3.1 for Apple silicon](https://github.com/dotcomjack/grux/releases/latest/download/Grux.dmg)** (Grux.dmg, 27 MB, macOS 14 or later) 1. Open `Grux.dmg`. 2. Drag Grux into Applications. 3. Open Grux from Applications. The disk image and the app are signed with a Developer ID, notarized by Apple and stapled, so Grux opens on a Mac that has never seen it, with no right click and no trip through System Settings. There is no installer and no updater phoning home. To check what you got before you run it: ```sh shasum -a 256 ~/Downloads/Grux.dmg # compare against the checksum in the release notes spctl -a -vv /Applications/Grux.app # /Applications/Grux.app: accepted # source=Notarized Developer ID ``` **Or through Homebrew**, which installs the app and the `grux` command together: ```sh brew install --cask dotcomjack/tap/grux ``` **The command line, after a DMG install.** It lives inside the app, so there is nothing else to download. This finds the app, puts `grux` on your PATH and runs setup: ```sh npx @dotcomjack/grux ``` Building from source is in [Building](#building) below, and it is the path to take if you want to change something rather than run it. ## Questions, answered Short answers to what people ask before they hand an agent their Mac. They live on [gruxai.com](https://gruxai.com/answers/), and every count in them is read from this repository when the site is built, and a weekly job flags any that have since moved. - [What is Grux OS?](https://gruxai.com/answers/what-is-grux/) - [A local AI assistant for macOS that runs on your own machine](https://gruxai.com/answers/local-ai-assistant-for-mac/) - [Which LLM can my Mac actually run?](https://gruxai.com/answers/which-llm-can-my-mac-run/) - [A free AI assistant with no account and no subscription](https://gruxai.com/answers/free-ai-assistant-with-no-account/) - [AI meeting notes without uploading the audio](https://gruxai.com/answers/ai-meeting-notes-without-uploading-audio/) - [Giving an AI agent your mail and calendar, on your own machine](https://gruxai.com/answers/give-an-ai-agent-your-mail-and-calendar/) - [Letting an AI agent run shell commands, safely](https://gruxai.com/answers/let-an-ai-agent-run-shell-commands-safely/) - [Stopping an AI agent leaking secrets from your screen and files](https://gruxai.com/answers/stop-an-ai-agent-leaking-secrets/) - [The macOS permissions an AI assistant asks for, and what each one buys](https://gruxai.com/answers/macos-permissions-an-ai-assistant-asks-for/) - [Driving a Mac app from the terminal with a coding agent](https://gruxai.com/answers/drive-a-mac-app-from-the-terminal-with-an-agent/) - [Open source AI agents for macOS, compared](https://gruxai.com/answers/open-source-ai-agent-for-macos/) - [Open source alternatives to the ChatGPT app on Mac](https://gruxai.com/answers/open-source-chatgpt-alternative-for-mac/) - [Mac apps that work with Ollama](https://gruxai.com/answers/mac-apps-that-use-ollama/) - [A Mac app you can customize with Claude Code](https://gruxai.com/answers/mac-app-you-can-customize-with-claude-code/) - [What is new in Grux 3.0](https://gruxai.com/answers/whats-new-in-grux-3/) Comparisons and how-tos, for people choosing between Mac AI apps or running a model locally: - [Grux vs Raycast AI: an open source alternative on Mac](https://gruxai.com/answers/grux-vs-raycast-ai/) - [Grux vs LM Studio: running a model versus an agent that acts](https://gruxai.com/answers/grux-vs-lm-studio/) - [Claude desktop alternative for Mac, open source: Grux OS](https://gruxai.com/answers/grux-vs-claude-desktop-app/) - [Grux vs the ChatGPT desktop app on Mac](https://gruxai.com/answers/grux-vs-chatgpt-desktop-app/) - [Grux vs Osaurus: two open source AI agents for Mac](https://gruxai.com/answers/grux-vs-osaurus/) - [An open source Siri alternative for Mac](https://gruxai.com/answers/open-source-siri-alternative-for-mac/) - [An open source Msty alternative for Mac: Grux OS vs Msty](https://gruxai.com/answers/grux-vs-msty/) - [An open source Jan alternative for Mac: Grux OS vs Jan](https://gruxai.com/answers/grux-vs-jan/) - [AI email assistant and client for Mac, open source and local](https://gruxai.com/answers/ai-email-assistant-for-mac/) - [Best local LLM app for Mac: Ollama, LM Studio, Jan, Msty](https://gruxai.com/answers/best-local-llm-app-for-mac/) - [MCP client for Mac: Grux OS, Claude, goose, LM Studio, Jan](https://gruxai.com/answers/mcp-client-for-mac/) - [Claude Code Mac app: the official app, and where Grux fits](https://gruxai.com/answers/claude-code-mac-app/) - [Ollama vs LM Studio on a Mac: which one to run](https://gruxai.com/answers/ollama-vs-lm-studio/) - [Ollama alternatives for Mac: LM Studio, llama.cpp, MLX, Jan](https://gruxai.com/answers/ollama-alternatives-for-mac/) - [How to run an LLM on a Mac (MacBook or Mac mini)](https://gruxai.com/answers/how-to-run-an-llm-on-a-mac/) - [Apple Intelligence alternative for Mac, open source and local](https://gruxai.com/answers/apple-intelligence-alternative-for-mac/) - [Whisper app for Mac: MacWhisper, whisper.cpp, WhisperKit](https://gruxai.com/answers/whisper-app-for-mac/) - [AI terminal for Mac: Warp, and a shell an agent can undo](https://gruxai.com/answers/ai-terminal-for-mac/) - [Local AI on a Mac mini: a home AI server and agent](https://gruxai.com/answers/local-ai-on-a-mac-mini/) - [AnythingLLM alternatives for Mac: Open WebUI, Msty, LM Studio](https://gruxai.com/answers/anythingllm-alternatives/) - [Open WebUI alternatives for Mac: native apps and self-hosted](https://gruxai.com/answers/open-webui-alternatives-for-mac/) - [How to run DeepSeek locally on a Mac (MacBook or Mac mini)](https://gruxai.com/answers/how-to-run-deepseek-on-a-mac/) - [How to run gpt-oss locally on a Mac (20b and 120b)](https://gruxai.com/answers/how-to-run-gpt-oss-on-a-mac/) - [Best local LLM for coding on a Mac, by memory](https://gruxai.com/answers/best-local-llm-for-coding-on-a-mac/) - [An AI agent to manage your calendar on a Mac](https://gruxai.com/answers/ai-agent-to-manage-my-calendar/) Every surface, one line each: [gruxai.com/features](https://gruxai.com/features/). ## Table of contents - [What it actually does](#what-it-actually-does) - [The command line](#the-command-line) - [What it costs](#what-it-costs) - [Requirements](#requirements) - [Building](#building) - [First run](#first-run) - [Permissions, and what each one buys](#permissions-and-what-each-one-buys) - [Privacy posture](#privacy-posture) - [Feature tiers](#feature-tiers) - [The phone companion](#the-phone-companion) - [Build on it](#build-on-it) - [Repository layout](#repository-layout) - [Tests](#tests) - [Who builds Grux](#who-builds-grux) - [Security](#security) - [Contributing](#contributing) - [License](#license) --- ## What it actually does The short version: one Command Panel that opens each surface as a pane beside it, and an assistant that can reach the things a Mac assistant should be able to reach. - **Chat** with tool use, against Anthropic or a local model. - **Local models** through Ollama, so you can run the whole thing without sending a token to anyone. - **Mailbox, Calendar, Contacts, Notes, Documents** against your real accounts, not a hosted copy of them. - **Meetings** that record both the microphone and system audio, transcribe on device with WhisperKit, and never upload the raw audio. - **Projects, Task Stack, Schedules, Skills, Commands** for the work around the work. - **Research** over the web, with a search key you supply. - **A shell** that runs real commands behind a path allowlist, a command denylist, a rate limit, and an audit log. - **Approvals**, which is where anything sensitive stops and waits for you. Every one of those is a real surface in the app. None of them is a stub. ## The command line Grux can be set up and driven entirely from the terminal, without opening a window. Shipped in 1.2.0, and the reason is not that a terminal is faster. It is that the coding agent already sitting in that terminal can do the work with you: install Grux, wire it into the rest of your machine, and go on extending it long after setup is done. Start here. One command, nothing to install first: ```sh npx @dotcomjack/grux ``` That finds Grux.app, puts `grux` on your PATH, and runs setup. After the first run, drop the `npx`. The launcher is a dependency free shim that only locates the binary and steps aside, so there is one implementation of everything below and no second front door to drift. Details in [npm/README.md](npm/README.md). The binary itself lives at `Grux.app/Contents/MacOS/grux-cli`, so you can link it by hand instead if you prefer: ```sh ln -s /Applications/Grux.app/Contents/MacOS/grux-cli ~/.local/bin/grux ``` ```sh grux setup # the whole first run, unattended grux status --json # what is configured, what is missing grux doctor # what is wrong, and what to do about it grux why # why a feature says it needs setup ``` - **Every command runs unattended.** A flag for every choice and JSON on every read, so nothing hangs waiting for a prompt an agent cannot see. - **Every command can print itself as a prompt** instead of running, so you can hand the job to whichever coding agent you already use rather than translating it yourself. - **An MCP bridge**, so an agent that speaks it can drive Grux through the same tools you do. - **Reads answer with Grux closed.** They come from files on disk. Writes go over a Unix socket at `0600`, so Grux still opens no network port. The full surface, every command and every exit code, is in [Grux-Mac/docs/cli-grammar.md](Grux-Mac/docs/cli-grammar.md). ## What it costs Nothing, to Grux. It is MIT licensed and there is no hosted component. You pay your model provider directly, at their prices, on your own account. Grux never proxies a request through anything we run, so there is no markup and no middleman with a copy of your prompts. If you run a local model through Ollama, it costs nothing at all. ## Requirements - macOS 14 (Sonoma) or later, Apple silicon - Xcode 16 or later (Swift 6.0+). `Package.swift` says `swift-tools-version:5.9`, but `swift-transformers` pulls `swift-jinja` 2.x, which is written against tools-version 6.0, so an older toolchain cannot resolve the dependency graph - An Anthropic API key, or Ollama running locally Dependencies are deliberately thin. Three direct ones. [WhisperKit](https://github.com/argmaxinc/WhisperKit) for on-device speech, which pulls in Apple's own packages plus HuggingFace's `swift-transformers`. And [grux-guardrails](https://github.com/dotcomjack/grux-guardrails), the secret redactor and URL policy, which is also ours, MIT, and has no dependencies of its own, so it adds exactly one node to the graph. And [swift-argument-parser](https://github.com/apple/swift-argument-parser), which the command line links directly rather than riding on WhisperKit's transitive pin. There is no analytics SDK, no crash reporter, and no telemetry package in the tree. You can check that yourself in `Grux-Mac/Package.resolved`. ## Building ```sh git clone https://github.com/dotcomjack/grux.git cd grux/Grux-Mac ./build.sh ``` That builds a release binary, assembles `Grux.app`, signs it, installs it to `/Applications`, and opens it. **On signing.** macOS keys every permission grant to the signing identity, so `build.sh` pins one to stop the app re-prompting on every rebuild. If you do not have that certificate, and you will not, it falls back to an ad hoc signature and says so. The app runs fine; you will just re-grant permissions after a rebuild. Set `GRUX_SIGN_ID` to your own identity hash to make grants stick: ```sh security find-identity -v -p codesigning # find yours GRUX_SIGN_ID= ./build.sh ``` **Distributable builds.** `GRUX_RELEASE=1 ./build.sh` signs with a `Developer ID Application` certificate and writes the artifact to your Desktop without installing it, because signing with a different identity would revoke the dev install's permission grants. Add `GRUX_NOTARIZE=1` to notarize and staple. Authentication is either an App Store Connect API key, which is preferred because it works unattended: ```sh GRUX_RELEASE=1 GRUX_NOTARIZE=1 \ ASC_KEY=~/private_keys/AuthKey_XXXXXXXXXX.p8 \ ASC_KEY_ID=XXXXXXXXXX ASC_ISSUER= ./build.sh ``` or an Apple ID with an app-specific password, via `APPLE_ID`, `APPLE_APP_PASSWORD` and `APPLE_TEAM_ID`. Apple notarizes only Developer ID signed code, so `GRUX_NOTARIZE=1` without `GRUX_RELEASE=1` is refused rather than uploaded and rejected. ## First run Grux opens on a short setup that asks before it assumes, and every screen in it can be skipped and come back to later. 1. **One question:** what you want Grux for. The answer picks the features to start with, and the next screen shows the pick before asking for anything, so you can change it. Would rather choose yourself? "I would rather pick from a list" asks how much to set up now instead. 2. **Your name and a model**, then a short look at how Grux works. 3. **Setup, one thing at a time:** only what the picked features need. Every macOS permission gets its own screen that says what it is for before macOS asks. **The minimum useful setup is one model.** Chat runs on an Anthropic API key, or on Ollama with no key at all. If you would rather not send anything to a hosted model at all, install [Ollama](https://ollama.com), pull a model, and Grux will use it. The Local Models tab picks it up automatically. Credentials go to the macOS Keychain under the service `com.gruxai.grux`. They are never written to disk in plaintext and never leave the machine except to the provider they belong to. ## Permissions, and what each one buys Grux asks for a lot, because it does a lot. Every one of these is optional, every one is requested only when you first use the feature that needs it, and refusing one disables exactly that feature and nothing else. Nine permissions, and only five are required by anything at all. The other four are asked for by a feature that still works without them, just with less in it. | Permission | Required by | What refusing costs you | |---|---|---| | Microphone | Meetings | Voice input in Chat and Reactor | | System audio capture | Meetings | Nothing else. This is the other half of the call, not your mic | | Screen Recording | Focus log | Screen context in Chat | | Calendar | Calendar | The agenda on Today, calendar tools in Chat and Reactor | | Contacts | Contacts | Contact lookup in Chat | | Automation | Nothing | Commands, and app control from Chat | | Accessibility | Nothing | Window and selection awareness in Chat, and detail in Focus log | | Notifications | Nothing | Alerts from Schedules, Workflows and Focus log | | Full Disk Access | Nothing | Jax Command, one BETA surface, and nothing else anywhere | That table is not typed by hand. `PermissionTableTests` parses it out of this file and asserts both columns against the same `FeatureRegistry` the app reads at runtime: the middle column must name exactly the features that list the permission as required, and the right column must name exactly the features that list it as optional. Move one without the other and the suite goes red. **Grux is not sandboxed.** ScreenCaptureKit, AppleEvents and cross app microphone access are not available inside the App Sandbox, so the OS level path allowlist is not available either. The filesystem boundary is therefore enforced in Swift, in one file, and that file is the only path from the model to your disk. It carries a read only root allowlist, a denylist covering `.ssh`, `.aws`, `.env`, the Keychain, Mail, Messages and browser profiles, a size cap, a rate limit, a secret pattern scan on everything it returns, and an audit log. That trade is written up in full in [SECURITY.md](Grux-Mac/SECURITY.md), including the parts it does not defend against. ## Privacy posture - **No account.** There is nothing to sign up for. - **No server.** There is no Grux backend. Nothing is proxied. - **No telemetry.** No analytics SDK, no crash reporter, no usage beacon. Grep the tree. - **Your keys stay in your Keychain**, and go only to the provider they belong to. - **Meeting audio is transcribed on device.** The recording does not leave the machine. - **Everything the model reads from disk is logged** to `~/Library/Application Support/Grux/fs-audit.log`, including the denials. You can read it at any time and it is plain text. The one thing to be clear eyed about: when you use a hosted model, that provider sees what you send it. Grux redacts secrets it recognises before anything goes out, but a hosted model is a third party by definition. Run Ollama if that matters to you. ## Feature tiers **Core (25).** Today, Chat, Approvals, Cognition Map, Projects, Task Stack, Mailbox, Calendar, Notes, Documents, Contacts, Schedules, Folders, Research, Skills, Compare, Local Models, Design Studio, Meetings, Speakers, Commands, Focus log, Integrations, Outbound Webhooks, Settings. **Labs (12).** Reactor, Jax Command, Feature Review, Agents, Compose and send, Media Studio, Social, Workflows, Self-Upgrade, Jax HQ, Meta Ads, Phone companion. Labs does not mean broken. It means the surface is real and the edges are not sanded. The app says so once: the Labs door carries one BETA badge for the surfaces behind it, and a labs feature that lives elsewhere (Agents behind the Developer door, Meta Ads and Social once you add a brand) keeps its own. Tests assert both halves, so the label cannot quietly go stale. ## The phone companion `GruxPhone/` is a small iOS app that pairs with the Mac over your local network and acts as a remote microphone and control surface. The link is Curve25519 key exchange, ChaCha20-Poly1305 encryption and HMAC authentication, and traffic never leaves your LAN. It is a labs feature and it is opt in. If you never pair a phone, the Mac never opens a listening socket. ## Build on it The reason this is MIT and not a download is that most of what is in here is plumbing, and plumbing is the part nobody wants to write twice. If you are building a Mac agent, the four modules below are usable without the app around them. **The pieces.** `GruxShellCore` is a PTY, the safety gates and the snapshot store. `GruxAgentCore` is orchestration. `GruxSetupCore` is the capability and permission model. `GruxMCPCore` is the read only MCP server. None of the four imports AppKit or SwiftUI, so they build and test without the UI stack, and all four are `.library` products in `Grux-Mac/Package.swift`. You can depend on one without taking the other three or the app. **To add a tool**, which is the most common thing anyone will want. Tools are declared in `ChatService.allTools()` in `Grux-Mac/Sources/Grux/ChatService.swift` and handled in the `switch` further down the same file. Copy `add_task`: the declaration is at the top of `allTools()` and its handler is the first `case`. Two edits, same file, and the model can call it. `ToolCatalogueTests` pins the count at 116, so adding one turns the suite red until you move the pin on purpose. **To add a surface**, meaning a place to open with its own screen. Add a `FeatureRow` to `FeatureRegistry.rows` in `Grux-Mac/Sources/Grux/Onboarding/FeatureRegistry.swift`, naming what it `requires` and what is merely `optional`. The setup dot, the setup sheet, the permission table in this README and the BETA badge are all read from that one entry, so getting the row right is the whole job. **What you must not break.** Four guards, and each one exists because the thing it catches already happened here: ```sh cd Grux-Mac swift test # 4038 tests python3 scripts/check-contract.py # the setup contract is frozen ``` The setup contract will not let a capability change meaning without a dated amendment in `Grux-Mac/docs/`. `PermissionTableTests` fails if the permission table above stops matching the registry. `NoPersonalIdentityTests` fails on a personal name anywhere in the shipping tree. The dash guard fails on an em dash or en dash, comments included. Several of them plant the fault they detect and assert they catch it, because a guard that cannot fail is not a guard. **What is unfinished, and where contributions help most.** Stated plainly, because it matters if you are deciding whether to contribute: - **A second model backend.** Chat talks to Anthropic or to Ollama. The seam is real but it has only ever had two implementations, so it is shaped by both of them rather than by the general case. A third would tell you where it is wrong. - **The MCP server is read only.** Writes go over a Unix socket instead. Making it a full bidirectional surface is a contained piece of work and it would let any agent drive the whole app. - **The 12 features behind the Labs door.** Every one of them is real and none of them is finished. Agents and Self-Upgrade are the two with the most left in them. - **Nothing here runs on an Intel Mac.** One arm64 slice, and no reason beyond nobody having needed it. If you build something on Grux, open an issue and tell us. We want to know. ## Repository layout ``` Grux-Mac/ the macOS app (Swift package, builds Grux.app) Sources/Grux/ the app itself Sources/GruxShellCore/ PTY shell, safety gates, snapshot store, no UI Sources/GruxAgentCore/ agent orchestration, no UI Sources/GruxMCPServer/ read only MCP server for external agents Tests/GruxTests/ the suite docs/ the setup contract and its change record scripts/ the frozen-contract checker SECURITY.md the threat model, in full GruxPhone/ the iOS companion ``` `GruxShellCore` and `GruxAgentCore` are deliberately free of AppKit and SwiftUI, so they can be used and tested without the UI stack. ## Tests ```sh cd Grux-Mac swift test ``` Be aware that `swift build` does **not** compile the test target, so a green build is not a green suite. The suite includes guards that are unusual and worth knowing about before you touch them: - The **setup contract** is frozen. `scripts/check-contract.py` fails if a capability changes meaning without a dated amendment in `docs/`. - The **identity scan** fails on a personal name, address or account identifier anywhere in the shipping tree. - The **dash guard** fails on an em dash or en dash anywhere that ships, comments included. - Several guards **test themselves**, by planting the thing they detect and asserting they catch it. A guard that cannot fail is not a guard. ## Who builds Grux Grux is built in Detroit as a DotcomJack project. DotcomJack has been shipping its own products since 2015, about a dozen of them today. That is why Grux exists. Running them was never bottlenecked on code. It was the hour a day spent moving between a mail client, a calendar, a terminal and four chat tabs that could not see any of it. Grux is the assistant built for that work: one that reads the window you are already in, handles the mail, and takes the meeting notes. Seven of the 37 features are the tooling that runs DotcomJack's own products: Jax Command, Jax HQ, Meta Ads, Social, Feature Review, Self-Upgrade and Workflows. They ship rather than being removed, because taking them out would misrepresent what you are downloading. It was eight until 3.0 removed the Domain monitor, and they are named here rather than only counted so the number cannot drift away from the list again. Grux has no account and nothing to buy. It is open source because it became useful enough to be worth other people's time, and because software that reads your screen and your mail should be readable back. Twelve features sit behind the Labs door because they are not finished yet. If something breaks, open an issue and tell us what you were doing. ## Security The threat model, the layered controls with file and line anchors, the denylist, the audit log format, and an explicit section on what Grux does **not** defend against are all in [SECURITY.md](Grux-Mac/SECURITY.md). **The two controls most likely to hurt you live in their own package, and Grux depends on it.** Keeping a credential out of a prompt, and stopping the agent following a hostile link, are [grux-guardrails](https://github.com/dotcomjack/grux-guardrails): 115 tests, zero dependencies, MIT, usable without any of the rest of this. Until 2026-09-06 that sentence would have been a lie by implication. Grux carried its own older copy of both controls and did not depend on the package at all, so six of the eight defects the advisories below describe were live in the shipped app, including both criticals. The copy is deleted and the app is on the package. `Package.resolved` is where you check that rather than taking our word for it. The reason to point you at it is not the test count. It carries [six published advisories](https://github.com/dotcomjack/grux-guardrails/security/advisories?state=published) against its own earlier releases, two of them critical, because six of the first nine tags failed to redact something. Those tags are still resolvable rather than deleted, so anyone pinned to one gets told by Dependabot instead of finding out some other way. A security library with a clean disclosure record after nine tags is a library nobody has attacked yet. To report a vulnerability, see [SECURITY](.github/SECURITY.md). Please do not open a public issue for anything exploitable. ## Contributing Yes, please. [CONTRIBUTING.md](CONTRIBUTING.md) covers the frozen contract, the house rules that are enforced by tests, and what a good pull request looks like here. It is short and it will save you a round trip. ## License MIT. See [LICENSE](LICENSE). ### Standing on other people's work Grux links the software below, and each of those licences asks to travel with it. MIT is permissive about what you may do with the code and not about the notice: retaining it is the whole of the obligation. Six of these are Apache 2.0, which asks for more, including reproducing any NOTICE file. The full licence text of every one of them, and the NOTICE files where they exist, are in [THIRD-PARTY-NOTICES.md](THIRD-PARTY-NOTICES.md), which also ships inside `Grux.app` so it reaches somebody who never opens this page. That file is generated from `Grux-Mac/Package.resolved`, and a test fails if it drifts, so a new dependency cannot ship uncredited. | Package | Licence | |---|---| | [grux-guardrails](https://github.com/dotcomjack/grux-guardrails) | MIT | | [swift-argument-parser](https://github.com/apple/swift-argument-parser) | Apache 2.0 | | [swift-asn1](https://github.com/apple/swift-asn1) | Apache 2.0 | | [swift-collections](https://github.com/apple/swift-collections) | Apache 2.0 | | [swift-crypto](https://github.com/apple/swift-crypto) | Apache 2.0 | | [swift-jinja](https://github.com/huggingface/swift-jinja) | Apache 2.0 | | [swift-transformers](https://github.com/huggingface/swift-transformers) | Apache 2.0 | | [whisperkit](https://github.com/argmaxinc/WhisperKit) | MIT | | [yyjson](https://github.com/ibireme/yyjson) | MIT | Thanks in particular to [WhisperKit](https://github.com/argmaxinc/WhisperKit), which is why meeting transcription runs on the machine and the audio never leaves it.