A native menu bar utility for managing background processes, SSH tunnels, and scheduled tasks.
- Tiny native macOS app with a Rust core (less than 1MB)
- Run any script or CLI task without keeping a terminal open
- Fire-and-forget one-time commands (silent, with notification, or in a terminal)
- Auto-discover scripts from a directory
- Run scripts on a schedule without cron, or launchd
- Controlled from the menu bar
- Cross-platform support (macOS, Linux, Windows)
- Everything is configured with one simple config file
On an Apple Silicon Mac, download something_bg-macos-arm64.zip from the GitHub Releases page, unzip it, then move Something in the Background.app to /Applications.
Starting with v1.10.1, GitHub release builds are signed with a Developer ID certificate and notarized by Apple before publication.
Starting with v1.11.0, the macOS app uses Sparkle for secure in-app updates. Open the status menu and choose Check for Updates... to run Sparkle's standard interactive update flow. The app also checks quietly at launch and once per hour; when it discovers a newer version, the menu action changes to Update Available... without interrupting you. The first v1.11.0 installation is manual because older versions do not contain the updater.
The packaged macOS release currently targets Apple Silicon (arm64). Intel Mac users can build the app from source on an Intel Mac.
Step 1: Install Rust (skip if already installed)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source $HOME/.cargo/envStep 2: Install build tools
xcode-select --install
cargo install cargo-bundleStep 3: Build and install
git clone https://github.com/vim-zz/something_bg.git
cd something_bg
./scripts/bundle-macos.sh
cp -r "target/release/bundle/osx/Something in the Background.app" /Applications/Launch from Applications or run: open "/Applications/Something in the Background.app"
Source builds do not require Sparkle. When Sparkle.framework is absent, the app continues to run and disables Check for Updates.... To build a Sparkle-enabled bundle, fetch the pinned framework and provide the public EdDSA key:
./scripts/fetch-sparkle.sh
SOMETHING_BG_SPARKLE_PUBLIC_ED_KEY="your_public_key" \
SOMETHING_BG_REQUIRE_SPARKLE=1 \
./scripts/bundle-macos.shFor local development, these variables may instead be stored in a git-ignored
.env file at the repository root. The macOS bundle and local Sparkle fixture
scripts load that file automatically.
The full replacement flow must use a bundled app, signed update ZIP, and HTTP appcast; cargo run cannot exercise it. After creating a Sparkle key pair, prepare the fixture without installing or launching anything:
SOMETHING_BG_SPARKLE_PUBLIC_ED_KEY="your_public_key" \
SOMETHING_BG_SPARKLE_PRIVATE_KEY="your_private_key" \
./scripts/prepare-local-sparkle-update.shThe script writes ignored files under target/sparkle-dev/ and prints separate commands to serve the fixture, install the seed bundle manually, and verify the replacement marker.
Download the latest Linux tarball from the GitHub Releases page, then extract and run the binary:
tar -xzf something_bg-linux-x86_64-unknown-linux-gnu.tar.gz
chmod +x something_bg_linux
./something_bg_linuxMove something_bg_linux somewhere on your PATH if you want to launch it more easily later.
Prerequisites (Ubuntu/Debian):
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source $HOME/.cargo/env
sudo apt update
sudo apt install build-essential pkg-config libayatana-appindicator3-dev libgtk-3-dev libxdo-devBuild and run:
git clone https://github.com/vim-zz/something_bg.git
cd something_bg
cargo run -p something_bg_linux --releaseBuild on Windows:
# Install Rust from https://rustup.rs/
git clone https://github.com/vim-zz/something_bg.git
cd something_bg
cargo build -p something_bg_windows --release
.\target\release\something_bg_windows.exeConfiguration is stored in ~/.config/something_bg/config.toml (created on first run).
version = 2
[environment]
path = "/bin:/usr/bin:/usr/local/bin:/opt/homebrew/bin"
[[sections]]
id = "database"
title = "DATABASE"
icon = "sf:cylinder.fill"
kind = "tunnel"
[[sections.items]]
id = "database-prod"
name = "PROD"
start = ["ssh", "-N", "-L", "5432:localhost:5432", "user@server.com"]
stop = ["pkill", "-f", "user@server.com"]
[[sections]]
id = "kubernetes"
title = "KUBERNETES"
icon = "sf:cloud.fill"
kind = "tunnel"
[[sections.items]]
id = "k8s-service"
name = "API Service"
start = ["kubectl", "port-forward", "svc/api", "8080:8080"]
stop = ["pkill", "-f", "svc/api"]
[[sections]]
id = "personal"
title = "PERSONAL"
icon = "sf:person.fill"
kind = "command"
[[sections.items]]
id = "fix-quarantine"
name = "Fix Whisperer Quarantine"
run = ["xattr", "-dr", "com.apple.quarantine", "/Applications/whisperer.app"]
[[sections.items]]
id = "deploy"
name = "Deploy"
run = ["bash", "/Users/me/scripts/deploy.sh"]
output = "terminal"
[[sections]]
id = "scheduled"
title = "SCHEDULED"
icon = "sf:clock.fill"
kind = "scheduled-task"
[[sections.items]]
id = "daily-backup"
name = "Daily Backup"
run = ["/usr/local/bin/backup.sh"]
cron = "0 6 * * *"version— Config schema version; the current version is2.sections— Ordered menu sections. The app inserts separators between them.- Section
id— Stable identifier, unique across sections. - Section
titleandicon— Optional visible heading and SF Symbol. - Section
kind—"tunnel","command", or"scheduled-task". - Item
id— Stable identifier, unique within its kind. - Item
name— Display name. - Tunnel
startandstop— Executable followed by its exact argument list. - Command
run— Executable followed by arguments;outputcontrols output handling. - Scheduled-task
runandcron— Command and five-field cron expression.
The order of [[sections]] and [[sections.items]] entries is the menu order. Commands are executed directly; use ["bash", "-c", "..."] when shell syntax such as pipes or && is required.
Legacy unversioned files and version = 1 files are migrated automatically. The original is retained as config.toml.v1.bak, while config.toml is rewritten in the current format.
Run any command with a single click from the menu bar. Each command has a configurable output mode:
| Mode | Behavior | Best for |
|---|---|---|
silent (default) |
Fire and forget, no output | Instant commands (xattr, pkill) |
notify |
Run in background, show notification on completion with last 5 lines of output | Scripts that take seconds to minutes |
terminal |
Open a terminal window with live output | Long/interactive scripts, debugging |
version = 2
[[sections]]
id = "utilities"
title = "UTILITIES"
kind = "command"
[[sections.items]]
id = "fix-quarantine"
name = "Fix Quarantine"
run = ["xattr", "-dr", "com.apple.quarantine", "/Applications/myapp.app"]
# output defaults to "silent"
[[sections.items]]
id = "backup"
name = "Run Backup"
run = ["bash", "/usr/local/bin/backup.sh"]
output = "notify"
[[sections.items]]
id = "deploy"
name = "Deploy"
run = ["bash", "/usr/local/bin/deploy.sh"]
output = "terminal"Auto-discover shell scripts from a directory. All *.sh files appear in the menu under a "Scripts" header, sorted alphabetically. Default output mode is notify.
version = 2
[scripts]
directory = "~/.config/something_bg/scripts"
output = "notify"
section = "scripts"
[[sections]]
id = "scripts"
title = "SCRIPTS"
icon = "sf:terminal.fill"
kind = "command"Filenames are title-cased for display: delete-logs.sh → "Delete Logs".
Common cron patterns:
0 * * * *— Every hour*/15 * * * *— Every 15 minutes0 6 * * *— Daily at 6am0 9 * * 1— Mondays at 9am
Common symbols for section icon:
sf:cylinder.fill— Databasesf:shippingbox.fill— Cache/Redissf:cloud.fill— Cloud/Kubernetessf:server.rack— Serversf:network— Networksf:clock.fill— Scheduled taskssf:hammer.fill— Development
Browse all symbols at developer.apple.com/sf-symbols or use the SF Symbols app.
Reload the configuration from the tray menu after editing the file.
MIT
