A native macOS menu bar app for managing HashiCorp Vault dev servers
vmenu lives in your menu bar and gives you one-click control over a Vault dev mode server. Start, stop, restart, check status, and copy environment variables, all without ever opening your terminal.
| Running | Stopped |
![]() |
![]() |
Though vmenu is just a cute and small menu bar app, it packs a lot of thoughtful features into a minimal surface area. Here are some of the things you can do:
- Start/stop/restart a Vault dev server with a click or keyboard shortcut.
- Server readiness menu bar icon indicator: green (unsealed), orange (sealed), red (stopped).
- One-click copy of
VAULT_ADDR,VAULT_CACERT, andVAULT_TOKENexport commands for Terminal session use. You can also copy or view the initial root token value right from the menu. - Server status at a glance: Vault version, seal status, storage backend, address, and unseal key along with raw status output.
- macOS-native: pure SwiftUI, lightweight, no Electron, no runtime dependencies.
- Fully sandboxed: the main app runs inside the App Sandbox; privileged operations are delegated to an unsandboxed XPC helper via
SMAppService. - launchd integration: manages Vault through a proper LaunchAgent.
- Keyboard shortcuts for every menu action (⌘S, ⌘R, ⌘I, ⌘Q).
The menu bar icon has a colored dot in the center that reflects one of these possible Vault server states:
| Icon | State |
|---|---|
| 🟢 Green | Vault is unsealed and ready for use. |
| 🟠 Orange | Vault is sealed and not available for use until unsealed. |
| 🔴 Red | Vault is stopped and not available for use. |
A Vault dev mode server managed with vmenu writes to 2 log files under your home folder at these paths:
$HOME/Library/Logs/vmenu/vault.startup.log$HOME/Library/Logs/vmenu/vault.operations.log
Tip
The vault.operations.log contains useful details written when you actually use Vault, whereas the vault.startup.log contains just the server startup information.
-
vmenu requires macOS 14 (Sonoma) or later and is tested on macOS 26 (Tahoe).
-
vmenu needs the
vaultbinary installed and available in yourPATH.
If you do not have Vault, you can install the binary with Homebrew:
brew install hashicorp/tap/vaultIf you do not use Homebrew, consider downloading a binary directly from releases.hashicorp.com/vault, and installing in your PATH using your preferred method.
Grab the latest DMG or zip from Releases, open the DMG, and drag vmenu.app into /Applications.
Note
Release builds from GitHub Actions are signed and notarized. If macOS still shows a Gatekeeper warning, right-click the app and choose Open, or go to System Settings → Privacy & Security and click Open Anyway.
Build both the main app and XPC helper
swift build -c releaseBuild and ad-hoc sign a full .app bundle (includes the XPC helper)
./build-app.sh releaseCopy app to /Applications folder.
cp -r vmenu.app /Applications/Note
The XPC helper agent (com.brianshumate.vmenu.helper) requires a properly assembled .app bundle so that SMAppService can find its LaunchAgent plist at Contents/Library/LaunchAgents/. Use ./build-app.sh to produce a complete bundle.
vmenu ships with a test suite; here's how to run the tests:
swift testDeveloper ID signing (for distribution)
Build with the first "Developer ID Application" identity in your keychain.
./build-app.sh release signBuild and explicitly specify an identity.
export CODESIGN_IDENTITY="Developer ID Application: Your Name (TEAMID)"; \
./build-app.sh release signThe build script signs both the XPC helper and the main binary with the same identity. The helper is signed first (inner component before outer bundle) with its own entitlements (vmenuhelper/vmenuhelper.entitlements).
The build script reads the latest git tag (e.g. v1.5) and stamps it into CFBundleShortVersionString and CFBundleVersion. If no tag exists, the version defaults to the value already in vmenu/Info.plist.
vmenu is a menu-bar-only app (LSUIElement = true), so it has no Dock icon or main window.
The app uses a two-process architecture for separation of concerns and defense in depth.
| Component | Binary | Sandbox | Role |
|---|---|---|---|
| Main app | vmenu | Sandboxed | UI, Vault HTTP API polling, clipboard |
| XPC helper | com.brianshumate.vmenu.helper | Unsandboxed | launchctl, plist/log file I/O, vault binary discovery |
The main app registers the helper agent via SMAppService.agent(plistName:) at launch. launchd starts the helper on demand when the main app connects to its Mach service over XPC.
The helper manages the Vault dev server through a LaunchAgent plist at ~/Library/LaunchAgents/com.hashicorp.vault.plist, using launchctl bootstrap/bootout/kickstart sub-commands.
The helper's launchd plist is embedded in the app bundle at Contents/Library/LaunchAgents/com.brianshumate.vmenu.helper.plist.
The main app communicates with the Vault server directly over HTTPS for status polling (/v1/sys/seal-status, /v1/sys/leader) without spawning any processes.
All operations that the App Sandbox forbids are exposed through the VmenuHelperProtocol XPC interface.
| Method | Operation |
|---|---|
findVaultPath |
Locate the vault binary on the system |
createOrUpdatePlist |
Write/update the Vault LaunchAgent plist |
bootstrapService / bootoutService / kickstartService |
launchctl lifecycle management |
checkServiceStatus |
Check if the Vault LaunchAgent is loaded |
readStartupLog / recreateStartupLog |
Read/reset log files for environment variable parsing |
readCACertData |
Read CA certificate bytes for TLS trust evaluation |
removeCACertFile |
Clean up stale dev-mode CA certificates |
vmenu is for managing a dev mode server, but it still strives to keep security in focus while doing so.
The main vmenu app runs inside an App Sandbox.
Operations that the sandbox forbids, like process spawning (launchctl), file I/O outside the container (~/Library/LaunchAgents/, ~/Library/Logs/vmenu/, CA cert files) are delegated to a dedicated XPC helper agent.
The entitlements for each component are documented in vmenu/vmenu.entitlements and vmenuhelper/vmenuhelper.entitlements.
| Component | Sandbox | Entitlements |
|---|---|---|
| Main app | Enabled | network.client (outbound HTTPS to 127.0.0.1:8200) |
| XPC helper | Disabled | Hardened runtime only (no sandbox) |
The app also uses the these defense-in-depth measures:
- App Sandbox on the main app restricts filesystem, process, and network access.
- Hardened Runtime enabled for both the main app and the XPC helper.
- Explicitly disabled unsigned executable memory and library validation bypass in both components.
- Ephemeral
URLSessionuse, so no credentials get cached to disk. - CA certificate path validation in the helper rejects symlinks, traversal, world-writable directories, and files with unsafe ownership or permissions.
- Log file safety: the helper uses
O_CREAT | O_EXCLfor atomic file creation and validates files are regular (not symlinks) before reading or writing. - XPC connection validation: the helper validates each incoming XPC connection against a code-signing requirement. Developer ID signed builds enforce the full Apple certificate chain (
anchor apple generic) and optional Team ID pinning. Ad-hoc signed builds verify the connecting process has the correct bundle identifier. - XPC isolation: the helper is registered via
SMAppService.agentand its Mach service is scoped to the app bundle. The main app invalidates the XPC connection on termination.
To completely remove vmenu and its associated files:
-
Quit vmenu if it's running (⌘Q or click Quit in the menu).
-
Stop the Vault server before quitting if it is running.
-
Remove the LaunchAgents (run in Terminal):
# Unload and remove the Vault LaunchAgent launchctl bootout gui/$(id -u) ~/Library/LaunchAgents/com.hashicorp.vault.plist 2>/dev/null && \ rm -f ~/Library/LaunchAgents/com.hashicorp.vault.plist # Unload and disable the vmenu helper LaunchAgent launchctl bootout gui/$(id -u)/com.brianshumate.vmenu.helper 2>/dev/null launchctl disable gui/$(id -u)/com.brianshumate.vmenu.helper 2>/dev/null
-
Remove log files (optional):
rm -rf ~/Library/Logs/vmenu -
Delete the app:
rm -rf /Applications/vmenu.app
Note
The Vault dev server generates a temporary CA certificate in /var/folders/ which macOS automatically cleans up, and so you do not need to manually remove that file.
Thanks to my friends at HashiCorp for inspiring me to build vmenu. I hope you also find it useful.
vmenu is an open source and personal project by Brian Shumate, and is not officially affiliated with Vault or HashiCorp in any way. As a 100% personal project, vmenu is not open to any contributions or feedback.
The author builds and manages this codebase with the support of coding agents.


