Open source · Apache-2.0 · runs on your machine

Always-on agents
you can actually trust.

Any model. Any MCP tool. Every action goes through a policy you wrote — allow, ask, or block — recorded, cost-capped, and sandboxed. Garu is systemd for your agents.

Try it in five minutes Star on GitHub
$ git clone https://github.com/humbertovillanueva/garu && cd garu && npm install && npm run build

The 45-second launch video (sound off here). Tomay preps your day; Bea files your mail and drafts replies that wait for one tap, and can't send. The control room's surfaces rebuilt for video; the meetings and emails are examples.

Why

Agents that work while you sleep are only useful if you can let them.

OpenAI's Dots and Meta's Muse run all day and act for you — closed, one vendor, unavailable in much of the world. Garu is the open version, built around the part they treat as an afterthought: control.

allow

A policy kernel in front of every tool

Rules by tool name and by argument: let an agent file mail but ask before each draft, write only briefs/2026-*.md, block everything else. Tools that can never pass policy are never shown to the model.

ask

An inbox, not a firehose

When a rule says ask, the run pauses. You see the email as an email, the file as a file, the draft as a draft. Approve once, approve for 24 hours, or decline. Silence is a deny.

block

Policy that learns, with you in control

Approve the same thing three times and Garu proposes the narrowest rule that covers exactly what you approved, and writes it into your file when you say so. Comments preserved.

recorder

A flight recorder

Every model turn, tool call, decision and approval in an append-only log, one file per run. Replay any run as a timeline, in the browser or with garu log.

$ cap

A budget on every run

Cost is estimated from real token counts at list price. A run stops the moment it would cross maxCostUsd. Local models bill $0 and still show what it would have cost.

sandbox

Any model, in a box

Gemini on the free tier, Claude, or Ollama on your laptop — one line to switch. Each tool server can run in its own Docker container: no network, read-only root, only the agent's workspace mounted.

How it works

One loop, one gate, one log.

An agent only touches the world through the Model Context Protocol. The policy kernel sits between the model and every MCP server. The flight recorder sees everything the kernel sees. The control room is a view over those files. There is no database.

you control room · CLI agent loop model policy kernel flight recorder · .garu/runs/*.jsonl allow → MCP tool server (Docker optional) ask → inbox → you · browser · phone · CLI block → never executed, never offered Gemini · Claude · Ollama
The Garufile

An agent is a small YAML file. The policy is the interesting part.

This is Bea, one of the agents in this repo: at 7:15 on weekdays she files the day's mail under four labels and drafts the replies. Reading and filing are allowed, every draft asks you, and her Gmail server has no send tool at all.

name: bea
persona: { kind: bee, tagline: "Sorts the inbox so you don't have to." }
model: gemini/gemini-3.5-flash-lite
triggers:
  - cron: "15 7 * * 1-5"
budget: { maxCostUsd: 0.05 }

tools:
  - name: gmail                 # Garu's own Gmail server
    command: node
    args: ["packages/mcp-google/dist/index.js", "gmail"]
    auth: oauth                   # sign in once: garu auth
    oauth:
      issuer: https://accounts.google.com
      clientId: ${GOOGLE_CLIENT_ID}
      clientSecret: ${GOOGLE_CLIENT_SECRET}
      scopes: [https://www.googleapis.com/auth/gmail.modify]

policy:
  - tool: "gmail.search_threads"  # reading: always fine
    action: allow
  - tool: "gmail.get_thread"
    action: allow
  - tool: "gmail.label_thread"    # filing comes off in one click
    action: allow
  - tool: "gmail.create_draft"    # words in your name: you see each one
    action: ask
  - tool: "*"
    action: block

prompt: |
  You are Bea. Sort Humberto's inbox from the last day…

First match wins

Rules are read top to bottom. Nothing matching means ask — never a silent allow.

Rules see arguments

when predicates (eq, matches with ignoreCase, gt, in, …) allow a tool for one host or one path and block it everywhere else.

Blocked means invisible

A tool no rule can ever allow is not offered to the model, so it never wastes a turn trying.

Secrets stay out of the file

${VAR} is read from .env at run time. The model never sees your keys, and a server that signs in gets only a one-hour token, never the client secret.

garu validate lints it

Unreachable rules, bad globs, a missing key — caught before the agent runs.

On your phone

Approve from bed. Or from the bus.

The control room installs to your home screen. It talks to the same garu ui on your computer over Tailscale — HTTPS, only your devices, nothing exposed to the internet. With ntfy set up, a push arrives when an agent pauses; one tap lands on the review card.

tailscale serve --bg 4000
# → https://your-mac.tail1234.ts.net
# Safari → Share → Add to Home Screen

Read the phone guide →

The control room on a phone: Bea's draft reply to Lena shown as an email, with To, Subject and the text, and Approve, Approve for 24h and Decline buttons above a four-tab bar.
Quickstart

Five minutes, free.

Node 22+ and a free Gemini key from aistudio.google.com (no card). Or skip the key and run a local model through Ollama for $0.

git clone https://github.com/humbertovillanueva/garu && cd garu
npm install && npm run build
printf 'GEMINI_API_KEY=your-key\nGARU_USER=YourName\n' > .env

npm run garu -- ui --up --as YourName       # control room at http://localhost:4000

Click pip in the sidebar and press Run now. It reads a notes file, pauses to ask before writing a summary, and you approve from the browser. Then type a message to it.

The real agents, Tomay (your day, from your calendar and email) and Bea (your inbox, sorted, replies drafted), need a free Google Cloud project once: the Google guide takes about fifteen minutes.

npm run garu -- run examples/nook/Garufile.yaml      # fully local with Ollama, $0
npm run garu -- run examples/vault/Garufile.yaml  # tools in Docker, network off
npm run garu -- log pip                                   # replay the flight recorder