---
title: "Mac network CLI, MCP and agent skills | Kilobat Docs"
url: "https://kilobat.app/docs"
description: "Install Kilobat for macOS. Explore the network diagnostics CLI, MCP server and AI agent skills for Wi-Fi, DNS, latency and bandwidth."
---

# Kilobat docs

Everything you need to run Kilobat from the menu bar, the terminal, or an AI agent.

## Install

The CLI is free and open source. Install it with Homebrew:

```
brew install seifip/kilobat/kilobat
```

This installs `kilobat` and `kb`, a short alias. Every example below works with either.

Or download the Mac app, which also includes the CLI at `Kilobat.app/Contents/Helpers/kilobat`.

[Download for Mac ↓](https://kilobat.app/downloads/Kilobat-1.9.0.dmg) [Download the free CLI ↓](https://kilobat.app/downloads/kilobat-cli-1.9.0-universal.tar.gz)

### Kilobat Lite on the Mac App Store

Kilobat Lite is a free edition for the Mac App Store, coming soon. It runs in Apple’s App Sandbox and keeps what works there: the health checks, the speed test, the Wi-Fi radar and nearby networks, “Why can’t it connect?” and keep awake. Per-app traffic, rules and the network filter, DNS changes, the lid-closed helper and the CLI need permissions the sandbox doesn’t allow, so they are only in the Kilobat app from this site. The two install side by side.

Turn on tab completion for your shell:

```
# zsh
echo 'source <(kilobat completion zsh)' >> ~/.zshrc
# fish
kilobat completion fish > ~/.config/fish/completions/kilobat.fish
```

## Quick start

Run `kilobat` on its own for an interactive menu: arrow keys or j/k to move, Enter to open, a number to jump, q to quit.

```
kilobat status --watch     # live traffic by app; q quit, s sort, f filter, / search
kilobat diagnose           # what is slowing your connection
kilobat speedtest          # speed and latency under load
kilobat profile travel     # switch to the Travel profile
kilobat block com.example.App
```

Run `kilobat <command> --help` for help with any command.

## Commands

### Monitor

|  |  |
|---|---|
| `kilobat status [--watch] [--json]` | Live per-app traffic and session budget; a JSON snapshot, or a JSON-lines stream with --watch |
| `kilobat report [--csv\|--json] [--summary] [--days 1…365]` | Locally recorded usage history, or daily totals and top apps; CSV has one row per app |
| `kilobat report compare --total 40GB [--unit GB\|GiB] [--direction both\|download\|upload] (--from DATE --to DATE \| --period last-month) [--network NAME] [--json\|--csv]` | A router's or provider's figure beside what Kilobat recorded on this Mac for the same period, in the same unit, with the hours recorded, every gap and likely reasons for a difference |
| `kilobat health-history [--hours 1…720] [--json]` | Router, internet, jitter and DNS checks the app recorded over time |
| `kilobat proxy [routes [--app name] \| test] [--json]` | Proxy apps, the system proxy and their problems; which rule and node each app took; how fast each node answers |
| `kilobat vpn [--json]` | The VPN, and whether DNS or IPv6 skip it |
| `kilobat calls [--limit 1…200] [--json]` | After each call: how many times it froze and what caused it, from the reports the app writes |
| `kilobat overlay [--port 7171] [--target internet\|router\|cs2:fra\|host]` | A live ping overlay for OBS and other streaming apps, served on localhost until Ctrl-C; a game region such as cs2:fra pings that data center |
| `kilobat support-report [--hours 1…168] [--host h] [--markdown\|--html] [--output f]` | A report for your provider or IT desk: findings, latency, outages and speed tests, without your network’s name or local addresses |
| `kilobat plan [--days 7…90] [--json]` | Speed tests against the plan you pay for on each network: the median, evenings beside the rest of the day, and how often a test got under half |
| `kilobat networks [forget <name>]` | Networks seen, with the profile, budget and data cost remembered for each |
| `kilobat networks cost <metered\|unmetered\|automatic> [name]` | Treat this network, or a named one, as metered or unmetered, or follow macOS again |
| `kilobat bandwidth [app] [hour\|day\|week\|month] [--back N] [--json]` | One app's sent and received bytes over an hour, day, week or month, with the peak; --back steps to earlier periods |
| `kilobat access-policy <bundle-id> [--json]` | The connections an app's developer declares in its Internet Access Policy, and why |

### Diagnose

|  |  |
|---|---|
| `kilobat diagnose [--json]` | Wi-Fi, router, internet, DNS and captive-portal checks with advice |
| `kilobat radar [--watch] [--json]` | Signal score and best-spot finder |
| `kilobat scan [--json]` | Nearby networks, channels and widths |
| `kilobat channels [--json]` | Congestion per channel and the least crowded one |
| `kilobat speedtest [--json] [--budget-mb 1…80] [--parallel]` | Cloudflare speed, loaded latency and call and streaming verdicts, 80 MB at most. One connection by default; --parallel opens four, as most speed-test sites do |
| `kilobat speed-compare [<test> <test>] [--json]` | What differed between two saved speed tests: network, time, server, connections, Wi-Fi band and signal, VPN, other traffic |
| `kilobat ip [--public] [--json]` | Where traffic leaves: the interface and any VPN, read on this Mac; --public also asks Cloudflare for your public IP and its registry country |
| `kilobat stutter [--seconds 10…60] [--target router\|internet\|<host>] [--json]` | Ping every 50 ms to find the short latency spikes speed tests miss, when they happen and whether they repeat |
| `kilobat starlink [--hours 1…720] [--json]` | A Starlink dish on this network, read from it locally: state, ground-station ping, alerts, the sky map and which way it’s blocked, and outages split into your setup, Starlink’s network and dish restarts |
| `kilobat games [--game cs2\|dota2\|deadlock\|fortnite] [--json]` | Ping each Counter-Strike 2, Dota 2, Deadlock and Fortnite data center five times and name the one closest to you, ready for the streaming overlay |
| `kilobat airdrop [status\|everyone\|contacts\|off] [--json]` | AirDrop visibility, the AirDrop link (AWDL) and what uses it; changing visibility opens System Settings |
| `kilobat bluetooth [--json]` | Bluetooth devices and whether they share 2.4 GHz with your Wi-Fi |
| `kilobat dns [automatic\|cloudflare\|google\|quad9\|flush]` | Read or set DNS, or flush the cache |

### Wi-Fi

|  |  |
|---|---|
| `kilobat wifi [status]` | Current network, signal, channel and link rate |
| `kilobat wifi on\|off\|restart\|disconnect` | Control Wi-Fi power and the connection |
| `kilobat wifi join <name> [--password-stdin]` | Join a saved network, or a new one |
| `kilobat wifi known` | Saved networks in order of preference |
| `kilobat wifi password\|share <name>` | Show a saved password, or a QR code to scan with a phone |
| `kilobat low-latency on [--for 15m\|1h\|2h] [--until-quit <app>]` | Pause AirDrop and AWDL during calls and games: 15 minutes by default, up to 4 hours, or until an app quits |
| `kilobat low-latency off` | Restore AirDrop and AWDL, also for a pause started in the app |

### Awake & agents

|  |  |
|---|---|
| `kilobat awake [--display]` | Keep the Mac awake until Ctrl-C; closing the lid still sleeps it unless you add --lid-closed |
| `kilobat awake --lid-closed [--for 2h\|--until-quiet\|-- <command>]` | Keep running with the lid closed too, 8 hours at most, through the app’s power helper; needs Allow running with the lid closed in Settings |
| `kilobat awake --for 90m\|2h\|45s` | Stay awake for a while |
| `kilobat awake --until-quiet [--app <id>] [--quiet 1…60]` | Stay awake until downloads or one app go quiet, below 16 KB/s for 3 minutes by default |
| `kilobat awake -- <command> [args]` | Stay awake while a command runs, then report the data its processes moved and exit with its status |
| `kilobat awake --while-frontmost <app> [--display] [--for 2h]` | Stay awake only while that app is in front, for reading or presenting. Switching away lets the Mac sleep; it ends when the app quits, at 20% battery or after --for. --display keeps the screen on too |
| `kilobat settings awake-on-power on\|off` | The app stays awake whenever the power adapter is in and lets the Mac sleep again when it comes out |
| `kilobat settings keep-screen-on on\|off` | Keep the screen on too for everything the app keeps awake: timers, downloads, agents, the app in front and plugged in |
| `kilobat sessions [--days N] [--json]` | Awake sessions and AI agent runs the app recorded, with data used and network changes |
| `kilobat agent-event <start\|working\|waiting\|done\|failed> [--agent name] [--stdin-json\|--json-arg]` | For an agent's hooks: tells the app a run started, waits for you or is done. Silent, always exits 0 |
| `kilobat why <url\|host> [--app <id>] [--proxy <url>] [--context container\|vm\|remote\|provider] [--failed-at <time>] [--json]` | Find the layer that stops a connection: network and interface, proxy and the app's own proxy, DNS, Kilobat rules, IPv4 and IPv6, TLS, the server and each redirect. Requests the exact URL; output hides query values |
| `kilobat conditions [--json]` | Metered, Low Data Mode, VPN, latency, battery and heat, whether the lid may close, with advice for long runs |

### Control

|  |  |
|---|---|
| `kilobat profile [travel\|work\|unrestricted]` | List or select a profile |
| `kilobat allow\|block <bundle-id> [--minutes 1…1440] [--profile name]` | Save an app rule, optionally temporary or for another profile |
| `kilobat reset <bundle-id> [--profile name]` | Remove an app's rule so it follows the default |
| `kilobat rules [--profile name] [--json]` | A profile’s rules |
| `kilobat limit [GB] [--cycle session\|daily\|weekly\|monthly] [--reset-day N] [--alert 50,80,100]` | Read or set this network’s data budget, when it starts over and when it notifies |
| `kilobat quota <bundle-id> <GB> [--upload] [--all-networks]` | Limit one app’s data per budget cycle; quota <bundle-id> --remove takes it off |
| `kilobat quota --list [--json]` | App quotas with what each used this cycle |
| `kilobat updaters [--json]` | Updaters paused on metered networks, the update bytes that still got through on this network, and the apps that update from inside themselves |
| `kilobat updaters on\|off` | Pause updater helpers such as Google Updater and macOS Software Update while the network is metered (on by default); the apps keep working |
| `kilobat settings [name [value] ]` | Read or change settings such as new-apps (allow, ask or block), apple-software, auto-travel, pause-updaters, awake-on-power, public-ip (off by default), speed-streams, alerts and menu-bar |
| `kilobat approvals [--json]` | Apps waiting for approval while new apps are asked about, with signer and destinations |
| `kilobat approve\|deny <app-id> [--minutes 1…1440]` | Block a waiting app at once, or ask the person to confirm allowing it in Kilobat |
| `kilobat block-host\|unblock-host <domain\|IP\|CIDR> [--app <id>]` | Block a domain (subdomains included, *.example.com accepted), an IP or a range for every app, or for one app |
| `kilobat quit <PID> [--force]` | Quit a process |

### Setup

|  |  |
|---|---|
| `kilobat open` | Open the Mac app |
| `kilobat doctor` | Check the app, network filter and monitoring |
| `kilobat completion [zsh\|bash\|fish]` | Print a shell completion script |
| `kilobat mcp` | Run the MCP server for AI agents over stdio |
| `kilobat update` | Update a Homebrew install |

### Expert

|  |  |
|---|---|
| `kilobat destinations [--pid PID] [--json]` | Observed process sockets |
| `kilobat devices [--bonjour] [--json]` | Devices on your network and Bonjour services |
| `kilobat devices name <MAC> <name>` | Name a device |
| `kilobat trace <host> [--rounds 2…10] [--json]` | Bounded 12-hop route probe; --rounds traces several times and shows each hop’s loss and delay |
| `kilobat route-history --host <host> [--json]` | Every saved trace to a host, hop by hop like mtr: loss, best, average, worst, route changes and where delay starts |
| `kilobat http-check <URL> [--json]` | DNS, connect, TLS and first-byte timing |
| `kilobat dns-benchmark` | Resolver timings |
| `kilobat lan-test <server> [--reverse] [--json]` | Five-second iperf3 test to a device running iperf3 -s; needs iperf3, which Kilobat never installs |
| `kilobat interfaces [--json]` | Physical and VPN counters, separately |
| `kilobat capture <interface> <file.pcap>` | Bounded packet capture |
| `kilobat connection-rule allow\|block <app> <host> [port]` | Save a destination rule |
| `kilobat blocklist status\|import <path>\|clear` | Manage a domain blocklist |
| `kilobat wake <MAC-address>` | Send a Wake-on-LAN packet |
| `kilobat spots [save <name>\|remove <id>]` | Saved Wi-Fi spots |
| `kilobat outages / speed-history [--days N]` | Recorded outages and speed tests as JSON |

## Scripting and JSON

Output switches to JSON automatically when piped, so scripts never parse the human layout. Pass `--json` to be explicit.

```
kilobat diagnose | jq .health            # "good", "degraded", "poor" or "unknown"
kilobat status --watch --json             # one JSON object per line, every second
kilobat report --summary --days 7 --json  # daily totals and top apps
kilobat report compare --total 40GB --period last-month --csv  # beside your provider's figure
```

Set `NO_COLOR` to turn off colors. Changes to DNS require administrator approval.

## AI agents and MCP

Step-by-step setup for [Claude Code](https://kilobat.app/agents/claude-code), [Codex](https://kilobat.app/agents/codex), [Cursor](https://kilobat.app/agents/cursor), [Gemini CLI](https://kilobat.app/agents/gemini-cli), [Claude Desktop](https://kilobat.app/agents/claude-desktop), [Windsurf](https://kilobat.app/agents/windsurf), [Cline](https://kilobat.app/agents/cline), [Zed](https://kilobat.app/agents/zed) and [any MCP client](https://kilobat.app/agents/mcp-client) is in [Set up Kilobat in your AI agent](https://kilobat.app/agents).

Give an agent like Claude Code, Codex or Cursor this prompt:

```
Install the Kilobat CLI with `brew install seifip/kilobat/kilobat`. Run `kilobat diagnose --json`, `kilobat status --json` and `kilobat scan --json`, then tell me what is slowing my connection and how to fix it. Ask me before changing DNS, profiles or rules.
```

### MCP server

`kilobat mcp` speaks the Model Context Protocol over stdio. Add it to Claude Code:

```
claude mcp add kilobat -- kilobat mcp
```

Or add it to any client that reads an `mcpServers` config:

```
{ "mcpServers": { "kilobat": { "command": "kilobat", "args": ["mcp"] } } }
```

Read-only tools: `get_status`, `diagnose_network`, `diagnose_connection`, `get_conditions`, `get_updaters`, `get_wifi`, `scan_wifi`, `suggest_wifi_channel`, `get_usage_report`, `compare_usage`, `get_app_bandwidth`, `get_access_policy`, `get_network_history`, `list_sessions`, `benchmark_dns`, `check_url`, `list_connections`, `list_devices`, `trace_route`, `get_route_history`, `compare_speed_tests`, `get_exit_path` (it asks Cloudflare for your public IP only when the agent passes `public_ip`), `list_networks`, `list_rules`, `list_approvals`, `list_blocked_connections`, `run_stutter_test` and `get_game_ping` (they only send pings), `get_nearby_radios` and `create_support_report` (the report for your provider, which the agent hands to you). They return the same JSON as the CLI. `run_speedtest` uses data, `run_lan_test` moves data on your own network for five seconds and asks first, and `keep_awake` and `release_awake` hold off idle sleep for a set time without changing settings; `keep_awake` with `lid_closed` keeps the Mac running with the lid shut too, once you allowed it, and asks you first. Change tools (`set_profile`, `set_app_rule`, `set_new_app_mode`, `decide_approval`, `block_destination`, `set_budget`, `set_app_quota`, `set_network_cost`, `set_updater_pause`, `set_dns`, which also flushes the cache, and `set_airdrop_visibility`, which opens AirDrop settings) are marked as changes, and their descriptions tell the agent to ask you first.

### When something can't connect

`kilobat why <host>` (MCP: `diagnose_connection`) checks one host layer by layer from this Mac, without running `curl` or `nc`, so a strict profile can't block the check itself. It tries IPv4 and IPv6 separately, so a network whose IPv6 is broken, a common cause of agents that hang or keep reconnecting, gets a plain answer. It names the interface used, such as Wi-Fi, a phone hotspot or a VPN, and tries each one when there are several. It reports the proxy the system or its PAC file picks, and with `--app`, the `HTTPS_PROXY` in that app's environment, without passwords. With a VPN or proxy on, it compares the direct path with the tunnel. A private name sent to a VPN's own resolver says which one, and whether it answers. Code in a container, VM or remote machine has its own network: pass `--context` and the report ends with a command to run there. When a hosted service makes the request from its own servers, as Warp does for a custom inference endpoint, `--context provider` says when the target is private (`localhost`, a LAN or Tailscale address) and so out of their reach, even though it works from the Mac.

Give it the exact URL that fails, query included: it requests that URL, but every output shows query values as `[redacted]`, so a signed download link stays private. It sends the request the way the app would: through the app's `HTTPS_PROXY`, the system proxy, or with `--proxy <url>`, a proxy address the app sets in its own settings. When that proxy works and direct connections don't, as on many company networks, it says so instead of failing; when direct connections work and the request through the app's proxy fails, it says the network is fine and the app's path isn't. A setting it can't read or go through, such as LM Studio's Hugging Face proxy switch for huggingface.co downloads, gets its own note citing what users reported, and the verdict then says the network reaches the host, not that the app's request works. Kilobat never changes another app's settings. A server that refuses HEAD gets one GET for a single byte, never a download. It follows up to five redirects, names the hop that fails and checks IPv4 and IPv6 for the host the redirects end at. With `--app`, it lists the app's open connections to that host or its proxy, says when they use another IP version or server than the fresh check, and looks again a few seconds later: a transfer that went quiet is reported as quiet, never as finished. The report records the app's name and version, the method it sent, the path it took and, with `--failed-at`, when the app failed beside when the check ran. A passing check means this request works from this Mac over that path now; it can't see how a transfer already under way is doing, so watch that with `kilobat bandwidth <app>`.

### Connect agents

Without help, Kilobat goes by an agent's traffic and CPU, so a quiet agent may be done or waiting for you. Its hooks tell Kilobat which project a run belongs to, when it waits for you and when it is done. Each hook runs `kilobat agent-event`, which prints nothing, returns at once and always exits 0, so it can't break the agent. If the app isn't running, events wait in a small file it reads when it opens. Kilobat reads session ids, folders and short status text, never prompts or replies. Merge these with any hooks you already have.

**Claude Code**, in `~/.claude/settings.json`. Each hook gets JSON on stdin with the session id and folder, which `--stdin-json` reads. A permission or input prompt means waiting; Stop means done once background tasks finish; a rate limit means waiting until it resets.

```
{
  "hooks": {
    "SessionStart": [{ "hooks": [{ "type": "command", "command": "kilobat agent-event --agent claude --stdin-json" }] }],
    "UserPromptSubmit": [{ "hooks": [{ "type": "command", "command": "kilobat agent-event --agent claude --stdin-json" }] }],
    "Notification": [{ "hooks": [{ "type": "command", "command": "kilobat agent-event --agent claude --stdin-json" }] }],
    "Stop": [{ "hooks": [{ "type": "command", "command": "kilobat agent-event --agent claude --stdin-json" }] }],
    "StopFailure": [{ "hooks": [{ "type": "command", "command": "kilobat agent-event --agent claude --stdin-json" }] }],
    "SessionEnd": [{ "hooks": [{ "type": "command", "command": "kilobat agent-event --agent claude --stdin-json" }] }]
  }
}
```

**Codex**, simplest: `notify` in `~/.codex/config.toml`. Codex passes a JSON argument after each turn, which `--json-arg` reads. It only says done, and Codex runs one `notify` program.

```
notify = ["kilobat", "agent-event", "done", "--agent", "codex", "--json-arg"]
```

For waiting too, use Codex hooks in `~/.codex/hooks.json` instead, and trust them with `/hooks`:

```
{
  "hooks": {
    "SessionStart": [{ "hooks": [{ "type": "command", "command": "kilobat agent-event --agent codex --stdin-json" }] }],
    "UserPromptSubmit": [{ "hooks": [{ "type": "command", "command": "kilobat agent-event --agent codex --stdin-json" }] }],
    "PermissionRequest": [{ "hooks": [{ "type": "command", "command": "kilobat agent-event --agent codex --stdin-json" }] }],
    "Stop": [{ "hooks": [{ "type": "command", "command": "kilobat agent-event --agent codex --stdin-json" }] }]
  }
}
```

**Gemini CLI**, in `~/.gemini/settings.json`. Gemini CLI runs as `node`, so Kilobat can't tie its traffic to the run, but the state comes through.

```
{
  "hooks": {
    "SessionStart": [{ "hooks": [{ "type": "command", "command": "kilobat agent-event --agent gemini --stdin-json" }] }],
    "BeforeAgent": [{ "hooks": [{ "type": "command", "command": "kilobat agent-event --agent gemini --stdin-json" }] }],
    "Notification": [{ "hooks": [{ "type": "command", "command": "kilobat agent-event --agent gemini --stdin-json" }] }],
    "AfterAgent": [{ "hooks": [{ "type": "command", "command": "kilobat agent-event --agent gemini --stdin-json" }] }]
  }
}
```

**Cline** (CLI, SDK and Kanban): install Kilobat's plugin, which adds one file to Cline's plugin store and changes no other settings. `cline plugin uninstall kilobat` removes it.

```
cline plugin install https://kilobat.app/agents/cline/kilobat.ts
```

It reports a run working, done, failed while Cline stays open, cancelled, rate-limited once Cline's own retries run out, and waiting while Cline asks you a question. It waits for subagents and teammates before calling a run done. Cline's approval prompts aren't visible to plugins, so traffic and CPU detection covers those.

Any other agent can call `kilobat agent-event start|working|waiting|done|failed --agent <name> --session <id> --cwd <folder>` from its own hooks. Add `--verbose` to see what happened when you try it by hand.

### Why speed tests disagree

Kilobat's speed test sends one HTTPS connection at a time to Cloudflare's nearest data center, 8 seconds each way and 80 MB at most, and pings 1.1.1.1 before and during the transfers. Speedtest by Ookla, fast.com and macOS's `networkQuality` open several connections at once and pick their own servers, so on fast or distant links they usually show more. Choose *4 connections* beside the test button in the Speed tab, or run `kilobat speedtest --parallel`, to see how much that matters on your connection. Each saved test keeps the Cloudflare data center, the number of connections, the link (Wi-Fi band, channel, signal and link rate, or Ethernet), the VPN it went through and how much other traffic this Mac moved meanwhile. History › Speed-test comparison, `kilobat speed-compare` and MCP `compare_speed_tests` set two tests side by side and list what differed, each with a note on how it changes a result. They list differences, not causes: other devices on your network compete too, and Kilobat can't see them.

### VPN and public IP

Health › Where traffic leaves, the Wi-Fi menu and `kilobat ip` show the interface the default route uses and whether that's a VPN, read from this Mac's routes and the VPNs macOS lists as connected. A VPN that's connected while the default route stays on Wi-Fi, such as Tailscale without an exit node, shows as Split: only some destinations go through it. Turn on Settings › Network › Look up public IP to also see the address websites see, asked from Cloudflare on network changes and every 30 minutes, with the country it is registered to from the DB-IP data bundled with Kilobat. It is off by default, since Cloudflare sees that request; `kilobat ip --public` asks once. The registry country isn't where you or a server are, and neither it nor a VPN shows that a connection is private or anonymous. Settings › Menu bar › VPN adds On, Split or Off (with the country code when the lookup is on) next to the bat, and History lists when a VPN went on or off and when the public IP changed.

### Route history

Tools › DNS & LAN › Trace 3 times, or `kilobat trace <host> --rounds 3`, traces a route three times with three probes a hop and saves each trace. History › Saved routes combines every saved trace to a host hop by hop, like mtr: loss, best, average, worst and last delay, which hops changed routers and where lasting delay starts. A hop that skips replies while later hops answer is a router rate-limiting probes, not loss. For the local network alone, `kilobat lan-test <device>` runs five seconds of iperf3 against a device running `iperf3 -s`; install iperf3 yourself, Kilobat never installs anything.

### While an app is in front

For reading or presenting, the teacup’s While an App Is in Front keeps the Mac awake only while the app you pick is the one in front, and lets it sleep again as soon as you switch away. Keep the screen on in the teacup decides whether the screen stays on too, as it does for everything Kilobat keeps awake. Kilobat follows macOS’s own app-switch events: it doesn’t poll, move the mouse or fake activity, so it can’t make you look present to monitoring software. It ends when the app quits, when you turn it off, or on battery at 20% (or the floor you set for lid-closed runs), and it never holds the lid. It has its own hold, so switching apps or quitting the app never ends an awake session or an agent run. From the terminal: `kilobat awake --while-frontmost Keynote --display`.

### While plugged in

Turn on Stay awake while plugged in (Settings › Awake & agents), or While Plugged In in the teacup, and plugging in the power adapter keeps the Mac awake until you unplug it, as the strip’s “Awake while plugged in” says. Keep the screen on decides whether the screen stays on too. Turn it off and it stays off until you next plug in, even if Kilobat restarts. Kilobat hears the switch from macOS’s own power-source notification, without polling. Unplugging ends only the session plugging in started: one you started yourself, a download you’re waiting on, an app in front, an agent run or a hold from the terminal carries on. It never keeps the Mac running with the lid closed. From the terminal: `kilobat settings awake-on-power on`, and `kilobat settings keep-screen-on on` for the screen; `kilobat conditions --json` reports it as `awakeOnPower`.

### Running with the lid closed

Closing the lid sleeps the Mac, even while Kilobat keeps it awake, unless you allow running with the lid closed in Settings › Awake & agents. That installs a small helper that runs as root; macOS asks you to allow it in System Settings › General › Login Items. With it on, a working agent run keeps the Mac going with the lid shut, and so does an awake session when you tick With the Lid Closed Too in the teacup menu. An agent waiting for you holds it only with Keep awake while an agent waits on, and for 30 minutes at most.

A closed Mac in a bag can overheat and run its battery flat, so the helper itself lets the Mac sleep at your battery floor (20% by default), when the Mac runs hot, as soon as it’s unplugged if you choose power only, after the longest stretch you set (8 hours at most), and a minute after Kilobat quits. If the helper or the Mac restarts while holding, it puts the setting back first. Kilobat shows how each hold ended, for example “Let the Mac sleep: battery at 20%”.

From the terminal, `kilobat awake --lid-closed` asks the app for the same hold and says why when it can’t; the hold ends with the command. Agents use `keep_awake` with `lid_closed`, which its description tells them to confirm with you first. `kilobat conditions --json` reports `lidClosedAllowed` and the current `lidHold`.

### Agent skills

Nine Agent Skills teach coding agents to troubleshoot a Mac's network and keep long runs going with Kilobat. They interpret its measurements, explain what is uncertain and ask before changing settings. Install them together; the `kilobat` skill sends the agent to the one that fits.

```
npx skills add seifip/kilobat-skills
```

|  |  |
|---|---|
| [kilobat](https://github.com/seifip/kilobat-skills/tree/main/skills/kilobat) | Start here. Setup, safety rules and which skill fits the question. |
| [mac-network-troubleshooting](https://github.com/seifip/kilobat-skills/tree/main/skills/mac-network-troubleshooting) | Slow or unreliable internet with an unclear cause: finds the failing layer. |
| [mac-wifi-troubleshooting](https://github.com/seifip/kilobat-skills/tree/main/skills/mac-wifi-troubleshooting) | Weak or dropping Wi-Fi, crowded channels, the best spot for the Mac or router. |
| [mac-hotspot-data-saver](https://github.com/seifip/kilobat-skills/tree/main/skills/mac-hotspot-data-saver) | Phone hotspots and data caps: what uses data, Travel profile, budgets. |
| [mac-dns-troubleshooting](https://github.com/seifip/kilobat-skills/tree/main/skills/mac-dns-troubleshooting) | Slow lookups, resolver benchmarks, switching DNS, captive portals. |
| [video-call-network-check](https://github.com/seifip/kilobat-skills/tree/main/skills/video-call-network-check) | Stuttering calls and game streams: latency spikes, jitter, loss, bufferbloat and background uploads. |
| [mac-bandwidth-monitor](https://github.com/seifip/kilobat-skills/tree/main/skills/mac-bandwidth-monitor) | Which apps use the connection, usage history and where they connect. |
| [mac-app-firewall](https://github.com/seifip/kilobat-skills/tree/main/skills/mac-app-firewall) | Block or allow apps, domains and IPs; ask about new apps and answer them. |
| [mac-agent-runs](https://github.com/seifip/kilobat-skills/tree/main/skills/mac-agent-runs) | Long agent runs: keep the Mac awake, check conditions, connect agent hooks, see when a run finished, waits for you or stalled. |

Install the Kilobat CLI separately with the Homebrew command above, or connect its MCP server. The CLI requires macOS 14 or later; the Homebrew source build requires Xcode 16 or later. See the [skills on skills.sh](https://skills.sh/seifip/kilobat-skills) or [read their source and references](https://github.com/seifip/kilobat-skills).

## The Mac app

1. Download the disk image, drag Kilobat into Applications and open it from there.
2. Live traffic, the Wi-Fi picker, speed tests, keep awake and Why can’t it connect? are free, and so is the whole CLI. Pro adds rules and Ask, budgets, Wi-Fi health, the connection map, radar and history in the app. To unlock Pro, start the 7-day trial or paste your license into Settings.
3. Choose your menu-bar items in Settings. Hold Command while dragging a menu-bar item to move it.

Allow Location access when asked so Kilobat can show Wi-Fi names and scan nearby networks. Your location is not sent anywhere.

### Bandwidth per app

Each app's row shows its last minute of traffic, upload above the line and download below. Click it for the app's echo: a sentence on what it moved, then a waveform of the last five minutes live, and with Pro its history by hour, day, week or month. Markers show when the Mac joined another network or an agent run used the app, and tiles show which networks carried the traffic, how it compares with the app's last four periods and its share of all traffic. Below the chart are the app's open connections with the data each has moved and its country, and the connections the app's developer declares in an Internet Access Policy, if it ships one.

### Comparing with your router or provider

When your router, ISP or carrier shows a different total from Kilobat, open History › Compare with your router or provider, or run `kilobat report compare`. Enter the figure as they show it, its unit and whether it counts download, upload or both, then the same period and the network it covers. Kilobat shows its own total in the same unit and the difference, and reads the figure in the other unit system too, since many providers write GB for 1,073,741,824 bytes (GiB) rather than 1,000,000,000.

A strip shows the hours Kilobat actually recorded and its gaps: time the Mac was asleep (recorded from Kilobat 1.3 on), time it was off or offline or Kilobat wasn’t running, time on other networks, and time before its history starts. Below it are the likely reasons for the difference, most likely first. Kilobat sees only this Mac, not the phones, TVs and other computers a router or provider counts. It leaves out local network and AirDrop traffic, which never reaches the Internet, and it counts packet headers no app accounts for as unattributed. Providers may count days in another time zone or post usage late. Kilobat doesn’t say which figure is right. Export the comparison as CSV or JSON, with the period, networks, recorded hours and every gap, to share with your provider.

### Stutter test

Calls, game streaming and remote desktops can stutter while speed and loss look fine, because a ping spike of a few hundred milliseconds disappears in an average. The stutter test in Health pings your router (or the internet) every 50 ms for 20 seconds and draws every reply, spikes in rose, with the median, p95, p99 and worst ping. It tells you whether the spikes repeat at a regular interval, which often points to AirDrop/AWDL scanning, or come at random, which points to a busy network, interference or a weak signal. Many routers answer only about ten pings a second; Kilobat recognizes that pattern and doesn't count it as loss. With Pro, *Compare with low-latency on* runs the test again with AirDrop/AWDL paused and says whether it helped.

The low-latency pause turns off AirDrop, Sidecar and Universal Control for 15 minutes, 1 or 2 hours, or until an app such as your game or call quits, 4 hours at most. It always restores when the time is up, the app quits, Kilobat quits or the network changes, and on the next launch if the Mac crashed. macOS asks for administrator approval. Health’s Nearby panel also shows whether AirDrop’s radio is active and what uses it, your AirDrop visibility, and, when you ask, the Bluetooth devices that share the 2.4 GHz band with Wi-Fi.

The Wi-Fi menu lists your Bluetooth devices under the networks: click one to connect or disconnect it. A 2.4 GHz tag marks connected headphones, keyboards and mice that share the band with your Wi-Fi, with a shortcut to a known 5 GHz network. Below them is AirDrop, with its visibility, what is using the link and the low-latency pause. Bluetooth appears only after you choose Show Bluetooth devices, so macOS asks for access only then.

### Proxies and VPNs

Kilobat works alongside proxy apps (Clash Verge, ClashX, mihomo, Mihomo Party, FlClash, Surge, sing-box, Stash, V2Ray, Xray, Shadowsocks) and VPNs. It only reads them; it never routes traffic or changes their settings.

- **Traffic counts for the right app.** An app that sends through a local proxy is charged for its own traffic, and the proxy core or VPN tunnel that forwards it is labelled “Carries other apps’ traffic” instead of being counted twice.
- **Which route each app took.** With a Clash-compatible API (Clash Verge, ClashX, mihomo, Mihomo Party, FlClash, sing-box’s clash_api), an app’s row shows the node it went through, such as “via HK-01 · 2 direct”, and its tooltip lists each host, node and rule. Kilobat reads the API address and secret from the app’s own config, and never shows the secret.
- **Problems, in Health’s Where traffic leaves:**

  - a system proxy that points at an app that isn’t running, which breaks every browser;
  - DNS or IPv6 that skip the VPN;
  - a proxy and a VPN both taking traffic;
  - global mode;
  - blocking that can’t see apps behind a system proxy. macOS filters never see connections to 127.0.0.1, so turn on the proxy’s TUN mode, or block the app in the proxy’s rules.
- **Why can’t it connect?** adds a Proxy app layer with the proxy’s mode and how fast its current nodes answer, and says when a dead node is the problem.

In the terminal: `kilobat proxy`, `kilobat proxy routes --app Telegram`, `kilobat proxy test` and `kilobat vpn`. For a core Kilobat doesn’t find on its own, pass `--controller 127.0.0.1:9090 --secret …`. Agents get the same through `get_proxy_status`, `test_proxy_nodes` and `check_vpn`.

### Widgets

Add Kilobat to your desktop or Notification Center: right-click the desktop, choose Edit Widgets and search for Kilobat. **Network status** shows the bat as the connection’s health with internet and router ping, and the medium size adds the last 30 minutes of latency, your last speed test and a Test button. **Data today** shows today’s internet data, down and up, by the hour; with Pro it shows your budget too. **Speed test** shows your last result and runs a new one in a click (10 MB on a metered network). The widgets show no network names or addresses, update when something changes, and say how old their figures are when Kilobat isn’t running.

### Call reports

After each call, Kilobat tells you whether it froze and why. It notices a call when Zoom, Microsoft Teams, FaceTime, Slack, Webex, Discord, Skype, WhatsApp, Telegram, Signal or a browser uses the microphone for more than ten seconds. It never hears the call: it only sees which app uses the microphone, which macOS shows anyway. Until the call ends it pings the internet and your router once a second, notes the busiest other app, and checks the Mac’s CPU, memory, temperature and GPU every five seconds.

Two or more seconds in a row of lost pings, or pings 200 ms slower than the call’s usual, count as a freeze. Each freeze gets a cause: another app uploading or downloading at the time, another device on your network (from your router’s traffic counters, when the router shares them over UPnP), the Wi-Fi (when the router stalled too), AirDrop scanning, or beyond your router. Separately, the card says when the Mac itself struggled: the CPU maxed out (and by which app), memory running out, the Mac slowing itself down because it’s hot, or the GPU maxed out. The card shows in Health › Calls and as a notification. Without Pro it shows how many times the call froze; Kilobat Pro names the causes and says what to try. Turn it off in Settings › Network › Report after each call. In the terminal, `kilobat calls` lists the cards, and agents get them through `get_call_reports`.

### Call mode

Call mode stops the apps that freeze calls while you’re on one. When a call starts, Kilobat suspends Dropbox, Google Drive, OneDrive, Box, Backblaze, Arq, Creative Cloud, torrent clients and similar apps that are running, and refuses their new connections, so an upload already under way stops at once. Game launchers (Steam, Epic Games, Battle.net) are held only while they download and no game is in front, so a game and its voice chat keep working. Everything resumes the moment the call ends, when you switch to one of the apps, when Kilobat quits, or after three hours at most; a list written before each pause makes sure nothing stays suspended, even after a crash. Apple’s own iCloud and update services aren’t suspended. Choose the apps under Settings › Network › Call mode › Choose…, where you can also pause AirDrop during calls (macOS asks for your password as each call starts). The call’s card then says which apps Call mode held. Call mode is part of Kilobat Pro.

### The check before meetings

Turn on Settings › Network › Check before meetings and allow Calendars once. About five minutes before an event with a Zoom, Google Meet, Microsoft Teams, FaceTime, Webex, Whereby or similar link, Kilobat spends ten seconds checking other apps’ traffic, the Wi-Fi, 100 quick pings to the internet, AirDrop and the Mac’s load. It notifies you only when something would freeze the call, such as “Dropbox is uploading and Wi-Fi is weak”; with Kilobat Pro the notification has a button that pauses those apps until the call ends. The calendar is read on this Mac and nothing about the meeting is kept or sent anywhere.

### Ping to game servers

Which region should you queue for? In Speed › Game servers, pick Counter-Strike 2, Dota 2, Deadlock or Fortnite and click Ping servers. Kilobat pings every data center of that game five times, a few at a time, and lists them by median ping with jitter and loss, the closest one first as **Best for you**. A steadier region within 2 ms beats a jumpier one. It takes about five seconds and runs only when you ask.

What it pings is each game’s own public target for the region: for CS2, Dota 2 and Deadlock, Valve’s relay in each data center, from the relay list Steam publishes for each game (their matchmaking traffic goes through these relays, and CS2’s own `net_print_sdr_ping_times` measures the same ones); for Fortnite, the ping server Epic names for each matchmaking subregion. So the number is your ping to that region’s data center, not to the match server you end up on, which can add a little. Where a network blocks ping, Fortnite falls back to timing a TCP connection to Epic’s ping server. Valorant, League of Legends and Apex Legends aren’t included: none has a public server that answers a ping, and the addresses players share don’t answer or aren’t confirmed, so Kilobat would only be guessing.

Click **Use on overlay** to put that ping on stream: the overlay then pings that data center once a second and names it on the pill, such as “CS2 · Frankfurt”. In the terminal, `kilobat games` (or `--game cs2`) prints the table and the overlay target, and `kilobat overlay --target cs2:fra` streams it; agents use `get_game_ping`.

### Focus filters

A Focus can change Kilobat while it’s on. In System Settings › Focus, pick a Focus such as Work, Do Not Disturb or one for travel, then Add Filter › Kilobat. Choose a profile to switch to, *Hold sync and download apps* (Dropbox, Google Drive, OneDrive, backups, game launchers and the other apps call mode holds, minus any you told it to leave alone; Steam, Epic and Battle.net only while they download and no game is in front), and *Pause updaters* (the updater helpers that wait on metered networks, here on every network while the network filter is on; Unrestricted allows them). When the Focus ends, macOS tells Kilobat, which brings back the profile from before, unless you picked another one meanwhile, and lets everything run again. A held app you switch to runs again for the rest of that Focus, and a call ending never lets go of an app the Focus still holds, nor the other way round. While a Focus filter is on, Settings › Network says so, for example “Focus: Work is holding Dropbox”. If Kilobat quits, everything goes back at once; if it crashes, it goes back at the next launch, and a Focus still on is applied again. macOS doesn’t say which Focus is on, so Kilobat names the profile. Focus filters need Kilobat Pro, except switching to Unrestricted.

### Starlink

On a Starlink dish’s network, Health shows a Starlink panel. Kilobat finds the dish by asking it for its status at 192.168.100.1, the address the Starlink app uses, when the network changes and every five minutes otherwise; without a dish the request fails in about a second and nothing shows. It reads the dish’s local API over gRPC-Web (port 9201) or gRPC (port 9200) on your own network, only reads, and never stows, reboots or reconfigures the dish. Nothing about it leaves your Mac.

- **Status.** Online, obstructed, searching or restarting; ping to Starlink’s ground station and how many of those pings were lost; what the dish moves right now for every device; how long it has been up; its software version.
- **Alerts** in plain words with what to do: slowed or shut down by heat, melting snow, motors stuck, a tilted mast, outside its service area, a slow cable, water, and an update waiting for a restart.
- **Sky map.** The dish’s own obstruction map in Kilobat’s colours: blue where it has seen satellites clearly, rose where something blocked them, with the direction and height in a sentence, such as “Blocked to the north-east, low in the sky, like a tree, a building or a hill.” With how often the sky is blocked and for how long.
- **Outages** from the dish’s log, kept on your Mac for 30 days, each sorted to the side that can fix it: **your setup** (obstructions, heat, power save, cable), **Starlink’s network** (no satellite in view, no reply from the ground station) or **dish restarts** (updates, repositioning). The report for your provider includes this log, without the dish’s serial number or location.
- **Calls.** A call freeze during a dish outage or obstruction is put down to it: something blocked the sky, no satellite was in view, or the dish restarted.
- **Stutter test.** Starlink switches satellites every 15 seconds, at :12, :27, :42 and :57 past the minute. When an internet test’s spikes land on those seconds, the verdict says so instead of pointing at your Wi-Fi or AirDrop.
- **Data plan.** Roam 100 GB, Roam 300 GB or Unlimited: a capped plan marks the network metered and, with Pro, sets a monthly budget of the cap. The budget counts this Mac; the panel also shows what the dish carried for every device in the last 15 minutes.

The panel, map, outages, CLI and MCP tool are free; naming each call freeze’s cause and budgets are part of Pro. With your own router in bypass mode, the dish is usually still reachable; if not, add a route to 192.168.100.0/24 through the router’s WAN port. In the terminal, `kilobat starlink` prints the same, and agents read it through `get_starlink_status`.

### Streaming overlay

Show your live ping on stream. Turn on Settings › Network › Streaming overlay, then add `http://localhost:7171/` as a Browser Source in OBS, Streamlabs or any streaming app (640 × 80 is plenty). A small pill shows ping, jitter, loss and the last minute as a line. It pings the internet (1.1.1.1) by default; pick your router or a game’s data center beside the setting, or from [Game servers](https://kilobat.app/docs#games), and the pill names the data center, which `server=0` in the URL hides. The bat at its start smiles in blue while the line is good, turns violet when it slows, and frowns in rose when it's poor. Change it in the URL: `?show=ping,jitter,loss,graph,speed`, `theme=dark`, `light` or `clear`, and `size=s`, `m` or `l`. With Pro, `brand=0` hides the wordmark and `face=0` shows a plain dot instead of the bat. While it's on, Kilobat pings its target once a second; the page is served on 127.0.0.1, so only this Mac can open it. Without the app, `kilobat overlay` serves the same page until Ctrl-C, with `--target` for the router or a game region.

### A report for your provider

When the connection is bad, your provider or IT desk wants proof. In Health, the report button makes one from what Kilobat recorded on this network over the last 24 hours or 7 days. It covers outages, latency at the router and beyond it, ping loss, and speed tests against your plan, plus a check of the site that fails if you add one. Each finding says what was measured and where, never whose fault it is. Copy it as text for a support chat, or save or share it as a PDF. It leaves out your network’s name, every local IP and hardware address, device names and which apps used the network. In the terminal, run `kilobat support-report --host github.com`, or `--html --output report.html` for the page.

### Plan check

Find out whether you get the speed you pay for. Type your plan’s download speed for this network in Settings › Network › Plan speed, or under a speed test result. The Speed tab then compares the last 30 days of tests on that network with it: “You got 61% of your 500 Mbps”, the median, the evening median beside the rest of the day, and how often a test got under half the plan. Each network keeps its own plan, so the café doesn’t count against your home plan. With Kilobat Pro, turn on Settings › Network › Plan check and Kilobat runs a light test up to three times a day, at a different time each day and always one in the evening peak (7 to 11 pm), when providers are most often slow. A check uses 25 to 80 MB over four connections, about two seconds at your plan’s speed. It waits until no call is going on, no VPN carries the traffic, the Mac’s other traffic is low and it’s on power or above 50% battery, and it never runs on a metered network. The provider report leads with the month: “Over 30 days, 41 scheduled checks: median 305 Mbps of the 500 Mbps you pay for (61%); evenings 180 Mbps.” In the terminal, `kilobat plan` shows the same, and agents get it through `get_plan_check`.

### Share cards

Share & Export in the menu bar's right-click menu makes a square image to post: the Weekly Echo (the data you moved, your busiest day and your hungriest app), your last speed test, or This place, a Wi-Fi report for the café, coworking space, hotel or library you're working from. This place shows the place's name, which you type or pick from Apple Maps search, then download, upload and ping from the newest speed test on this network, whether it's good enough for video calls, how it holds up under load, and the Wi-Fi band. Pick the place from the search and the card gets a small map of its neighbourhood with a dot on it; turn off Show map to leave it out. Add tags such as Power outlets, Quiet or Open late, and a one-line note. Kilobat remembers them for that network, on this Mac. Copy the card, save it or share it to Nomad List, a Google Maps review or a coworking chat. No card shows a network name or an address. The map shows the place you picked, never where you are, unless you choose Use my location, the only time This place asks for Location.

Your year in Kilobat sums up the year on one card: the data you moved with a bar for each month, your hungriest app, how many networks you used, your busiest month, your best café Wi-Fi and your longest streak of video calls without a freeze. The best café is the fastest speed test at a place you named on a This place card, shown with the name you typed; if you never named one, the card shows your top speed and says only “on my fastest network”. A stat with nothing to show is left out. When Kilobat's history starts after the first week of January, the card says “since August” rather than claiming the whole year, and in January it still shows the year that just ended. It is always in Share & Export. In December it moves to the top of that menu and the Share sheet, and once, early in the month, Kilobat sends one notification that it's ready, only if you already allow Kilobat's notifications.

Other apps can open Kilobat at the right place with a `kilobat://` link (from 1.3.4): `kilobat://open/speed` opens a tab (overview, connections, health, radar, speed, history, tools or settings), and `kilobat://share/speed`, `kilobat://share/week`, `kilobat://share/place` or `kilobat://share/wrapped` (your year in Kilobat) opens a share card, and `kilobat://speedtest` runs a speed test. Run one from Terminal with `open kilobat://open/history`, or bind it to a key in a launcher. The Raycast extension uses these links for its Open in Kilobat actions, and [Kilobat’s Shortcuts actions](https://kilobat.app/shortcuts) run checks, speed tests, keep awake and reports from the Shortcuts app.

### Where it connects

Connections shows every open connection on a globe, routed to the country its address is registered in, with the busiest countries in a list and the sockets underneath. Click a country to filter. The country comes from DB-IP's free database bundled with the app, so no address leaves your Mac. It is the registration country: a CDN or VPN address shows its owner's country, not where the server is.

## Blocking and filtering

Monitoring works without filtering. To enforce rules, open filtering setup in Kilobat and approve the extension and content filter when macOS asks. On current macOS, extensions are under System Settings → General → Login Items & Extensions → Network Extensions.

Filtering affects new connections; an existing connection may continue until it reconnects. If a rule interrupts something important, switch to Unrestricted or turn filtering off in Settings.

### New apps: allow, ask or block

The pill above the app list sets what Travel and Work do with apps that have no rule. **Allow** lets them connect. **Ask** holds an app's first connections and opens a window with who signed it, its path and where it wants to go; allow or block it once and Kilobat saves the rule. Unanswered connections give up after a minute and the app asks again. **Block** only lets apps you allowed connect. Turn on *Allow Apple software* to let macOS services and Apple's apps through without asking. Your blocklist and connection rules apply in every mode, and if Kilobat isn't running, new apps are allowed rather than held.

### Hotspots and data cost

macOS flags some connections as metered, such as an iPhone hotspot, but not every Android hotspot and not a phone tethered over USB or Ethernet. Each network has a data cost: *Automatic* follows macOS, and *Metered* or *Unmetered* is your call. Set it in the Wi-Fi menu, the budget menu, the Health tab or Settings, or with `kilobat networks cost metered`. On a metered network Kilobat switches to Travel, unless you chose a profile for that network, brings your previous profile back when you leave, and skips scheduled speed tests. `kilobat conditions --json` and the MCP server report both `metered` and what macOS says (`osMetered`). Kilobat needs to tell a network apart before you can set its cost, so for Wi-Fi it needs Location access to see the network’s name.

### Pausing updaters on metered networks

On a metered network, the network filter also pauses the helpers that download updates on their own: Google Updater (and the older Keystone agent), Microsoft AutoUpdate, macOS Software Update (`softwareupdated`), App Store downloads (`appstored`, `appstoreagent`, `storedownloadd`), Sparkle’s download service, and Sparkle’s and Squirrel’s installers (`Autoupdate`, `Updater.app`, `ShipIt`). They are blocked with an ordinary filter rule, the blocked-connection journal says “Updater paused on a metered network”, and the apps themselves keep working. Once the network isn’t metered, updates carry on. Nothing in other apps, their staging folders or System Settings is changed, so there is nothing to restore. It is on by default; turn it off in Settings or with `kilobat updaters off`. Rules that already apply to a connection, such as a destination rule or a temporary allow, come first, so “allow for 30 minutes” lets one update through. Unrestricted pauses nothing.

The Health tab, `kilobat updaters` and the MCP tool `get_updaters` show which updaters are covered, which update bytes still got through on this network (an updater’s own bytes, and System downloads such as `nsurlsessiond` that connected for one, whose bytes include other downloads), and which installed apps update from inside their own process. Most apps built on Sparkle or Electron download the update inside the app and leave only the install to a helper, so they can’t be paused without blocking the app. The filter stops new connections, so a download already running when the network became metered can finish. Kilobat’s own scheduled update check waits for an unmetered network too; Check for Updates still works by hand.

### Data budgets and app quotas

Each network has its own budget. By default it counts until you reset it. Set it to start over daily, weekly on a weekday you choose, or monthly on the day your plan renews; a plan that renews on the 31st starts on the last day of shorter months. A budget with a cycle counts the usage history of that network, so it carries on after a restart. Kilobat notifies once per cycle at 80% and 100% by default, and at 50% if you add it.

An app can have its own quota: a limit per cycle on everything it moves or only its uploads, on one network or on all of them. Set one from the app’s bandwidth panel or with `kilobat quota com.getdropbox.dropbox 2 --upload`. With filtering on, a used-up budget stops new connections, and a used-up quota stops that app’s new connections. A temporary allow or a connection rule still gets through. Kilobat samples traffic every second, so budgets and quotas are approximate: an app can go a little over before it stops.

### What counts: Internet, local network and VPNs

Budgets, quotas and data used count Internet traffic only. Traffic to your local network (printers, NAS, other computers, a router's admin page), AirDrop and other nearby-device links (AWDL) is shown apart, as “Local network: 2.1 GB, not counted”, when you hold the pointer over the budget card's used figure, and in each app's bandwidth panel. The Mac's total comes from the Wi-Fi and Ethernet counters, so it includes what no app accounts for: packet headers and acknowledgments, which apps' byte counts leave out (usually 5–10% more), a process that started and finished between two samples, Internet Sharing, or a VPN that macOS runs itself. The same tooltip shows that part as “Not tied to an app”; it is never assigned to an app.

With a VPN, an app's traffic goes through the tunnel and the VPN carries it out over Wi-Fi or Ethernet. Kilobat counts it once, where it leaves the Mac, as your carrier does, and labels the app's share “Through a VPN”. With a proxy (a system proxy, `HTTPS_PROXY`, or a local proxy app), apps talk to the proxy on this Mac and the proxy makes the Internet connections, so the bytes show under the proxy; Kilobat can't tell which app was behind it. A proxy in TUN mode is a VPN to Kilobat: each app's own connection is labelled “Through a VPN”, and the proxy's outbound traffic shows under the proxy. A company proxy on a private address counts as local network.

### System downloads

Apps hand some downloads to macOS: `nsurlsessiond` (background transfers for apps such as Music and Podcasts), `mobileassetd` (Apple Intelligence and Siri models, voices, fonts), `softwareupdated`, `appstored` and `appstoreagent`, `cloudd` and `bird` (iCloud), `fileproviderd` and Content Caching. The app list groups them as System downloads, with what each does. With filtering on, the filter sees which app each connection was for, so the list says “For Music” and a block reads “com.apple.Music via nsurlsessiond”. Without filtering, nettop sees only the service, so Kilobat can't say which app a download was for.

A process that is neither an app nor part of macOS, such as `node`, `python`, `deno`, `bun` or a Go binary in your home folder or Homebrew, shows its executable path when you expand its row, so an unfamiliar agent or tool stands out.

Turn on *Unusual uploads* in Settings to hear when an app uploads five times its usual in an hour, and at least 50 MB, at most once a day per app. It only notifies. Temporary rules and expiring connection rules are listed under Profiles with the time left, so you can end one early.

### Blocking domains and IPs

Right-click an app's open connection to block that host for the app or for every app. The blocklist takes domains (subdomains included; `*.example.com` works too), IP addresses and CIDR ranges, alongside an imported hosts list. From the terminal: `kilobat block-host ads.example.com`.

## Give Pro, get Pro

Every Pro license has its own referral link, `kilobat.app/r/` and a short code. Find it in Settings under Kilobat Pro, or below a share card, with Copy link and Share. There is no account: the code comes from your license, and it doesn’t reveal your key.

A friend who opens your link and then Kilobat gets a 30-day Pro trial instead of 7. Each Mac can use one referral link, once. When they buy Pro, from the app or from the site after following your link, your license gets one more Mac. Every third friend earns you a free Pro license: a single-use code shown in Settings, to enter at checkout for yourself or anyone you like.

A purchase counts once the 14-day refund window has passed; until then Settings shows it as waiting. Refunded purchases, free licenses and buying through your own link don’t count.

## Privacy

Traffic history, network names and rules stay on your Mac. There is no account and no telemetry. Diagnostics contact test services, and license activation and update checks contact their services. Read the [privacy notice](https://kilobat.app/privacy).
