Menu Bar Guide
How each tool type renders in the Runyard popover, and what every action does.
Runyard's entire UI lives in the menu bar. Click the icon to open the popover; press Escape or click outside it to dismiss.
The Runyard icon
Left-click the icon to toggle the popover. Alongside the icon you may see, depending on your settings:
- A short status text (e.g.,
2 runningorStarting Backend, Frontend…). Toggleable in Settings → General → Menu Bar. - A red ⚠︎ triangle whenever any health check is failing or a service is in the
Errorstate. - A ☕ filled cup whenever you've manually activated Keep Awake (see below). Service- and probe-triggered Keep Awake sessions do not show this glyph. It's reserved for the on-demand toggle so the menu bar reads "I switched it on", not "something I configured is running". Hide the glyph in Settings → General → Menu Bar.
- A dynamic hover panel listing every running tool, the current Keep Awake state, and any failing health checks.
The status text is frozen while the popover is open. It refreshes once you close the popover. (Touching the title would resize the menu-bar button and visually disconnect the open popover from the icon, so the refresh is deferred.)
Popover layout
The popover is a fixed-width column with four sections:
- Header: the brand line, an optional
N running · M errorssummary, and a tri-state Collapse all / Expand all button that toggles every card at once. Each card also has its own chevron and remembers its collapsed state across launches. - Cards: one per top-level tool, in the order they appear in
config.json. Long configs scroll vertically inside the popover. The maximum height adapts to your display: at least 600 pt, and more on tall screens (the cap isscreen-height − 200so the popover never overflows the visible area). - Keep Awake toggle: a row just above the footer that activates or deactivates
caffeinateon demand (see Keep Awake). - Footer: four icon tiles: Settings, Reload, Updates, and Quit.
The four tool types
Service
A service is a managed process: a backend, a dev server, a Docker stack. It renders as a card with:
- A header line: tool name + a status pill (
Running/Stopped/Starting/Stopping/Error). - A meta-line under the header showing one state-dot per
startCommand(Backend, Frontend, …) plus the detected port as a:3001blue tag once the tool is running. - An action chip-row of any actions you've defined, filtered by the action's
showWhen. The first URL action gets the primary blue accent. - A footer button-bar with state-dependent controls: Stop (running), Start (stopped), Start + Clear Error (errored), plus View Logs in every state.
When the card is collapsed, the header compresses to dot · name · port · pill so you can see the gist at a glance.
Shortcut
A shortcut is a flat list of links or actions under an uppercase section label. No process management. Each action renders as a full-width tile in a vertical stack, regardless of action type (URL, command, AppleScript, reveal, or health check). Top-level shortcut cards have their own chevron and participate in the Collapse all / Expand all toggle. Shortcuts nested inside a group render flat without a chevron; collapse the parent group instead.
{
"name": "Quick Links",
"type": "shortcut",
"actions": [
{ "label": "Open Grafana", "type": "url", "url": "https://grafana.example.com" },
{ "label": "Open Jira", "type": "url", "url": "https://jira.example.com" }
]
}
Group
A group bundles related tools under one collapsible card. The header shows an aggregate status pill that adapts to the group's contents:
- Services-only group →
running/total(e.g.1/2) - Health-checks-only group →
healthy/total - Mixed →
running/total · healthy/total - Any nested service errored or health check failing → orange pill with a
· ⚠suffix - Empty group → no pill
Below the header, each child renders as a single compact row to keep groups dense:
● Service Name :8080 [PILL] ⏹ 📄
● Health Check Name 124 ms [PILL] ⏸ ↻ 📄
Layout per child:
- Services: state-dot, name, optional
:porttag, status pill, then inline icon buttons. Stop (stop.fill) when running, Start (play.fill, blue accent) when stopped or errored, plus View Logs (doc.text). - Health checks: state-dot, name, latency (omitted when paused), status pill, then Pause / Resume (
pause.fill/play.fill), Check now (arrow.clockwise, blue accent), and View Logs (doc.text). - Shortcuts: an uppercase section label and a chip-row of actions (no compact form, since shortcuts have no state).
A second chip row appears below the compact row only when the service has visible config-defined actions (gated by showWhen) and/or is in .error state. Errored services get a red Clear Error chip at the end of the second row.
Right-click any compact row to get a full text menu with all actions plus extras: Reveal Directory in Finder, Copy Start Command (services), Copy Endpoint URL (health checks).
Group-level actions live in a card footer under an uppercase ACTIONS section title. The buttons render as a 2-column grid (single actions span the full width; an odd third action gets its own full-width row). The first URL action gets the primary blue accent, and healthCheck actions render with their own inline status. Clear Errors (N) appears as a red full-width row at the bottom when ≥ 2 nested services are errored or have cached health-check failures.
Tip: nested health checks don't render the 20-bar latency sparkline (it would balloon group height). If you want the sparkline plus the full card UI, promote the health check to top-level in
config.json(move it out of the parent group'stoolsarray).
{
"name": "QA Stack",
"type": "group",
"actions": [
{ "label": "Open Dashboard", "type": "url", "url": "https://qa.example.com" }
],
"tools": [
{ "name": "API Server", "type": "service", "...": "..." },
{ "name": "Links", "type": "shortcut", "...": "..." },
{ "name": "Health", "type": "healthCheck", "http": { "url": "..." } }
]
}
Groups cannot be nested inside other groups.
Health check
A health check polls an HTTP or TCP endpoint on a fixed interval and renders as its own card with:
- A header showing the health-check name and a Healthy / Failing / Paused / Unknown pill.
- A meta-line:
247 ms · checked 12s ago · every 30s(the relative timestamp ticks every second while the popover is open). - A 20-bar sparkline of the most recent latencies: green for successful samples, red full-height bars for failures (timeouts / connection refused). Heights normalize to the 95th percentile so a single outlier doesn't flatten the rest.
- A footer button-bar: Pause / Resume, Check now, Logs.
Health checks nested inside groups render as compact rows (no sparkline). Promote one to top-level if you want the sparkline + full card UI.
Pause and Resume are runtime-only controls: they do not modify your config.json. On next launch the probe respects the Auto-start on launch setting from Settings → Tools.
When any health check is failing, a red ⚠︎ triangle appears next to the Runyard menu bar icon.
Response time stats
Hover any health-check tile to see response-time statistics from a rolling 1-hour window of recent checks. On a collapsed healthy tile hover the sparkline; on a failing, paused, or expanded tile hover the status pill. The window is fixed (not configurable) and resets when Runyard quits.
With fewer than 5 successful checks, the popover shows the latest latency on its own with a "Need N more checks…" countdown. Tail and average estimates need at least 5 samples to be meaningful.
With 5 or more successful checks, the popover shows a four-metric breakdown:
- Typical (p50): the median. Half of recent checks were faster, half slower. Shown as the hero number with a
TYPICALlabel. The most honest estimate of a normal response time, unaffected by single outliers. - Slow tail (p95): 95th percentile. Only 5% of checks were slower than this. A leading indicator of intermittent slowdowns.
- Average: arithmetic mean of successful checks. Useful, but easily skewed by a single slow check.
- Failure rate: count of failed checks in the window. Shown only when at least one check has failed.
The Slow tail, Average, and Failure rate rows each show a brief italic explanation underneath.
When a check is failing or paused, the popover swaps to the most relevant info for that state (last error, time since last success, and the consecutive-failure count when failing; the last-known stats dimmed when paused).
Actions
Actions are extra menu items per tool. Each action has a label and exactly one type:
| Type | Field | What it does |
|---|---|---|
| Open URL | url |
Opens the URL in your default browser. Supports {{port}} placeholders. |
| Run command | command + args |
Runs a shell command (in the tool's directory by default). Output goes to an action log. |
| Reveal in Finder | reveal |
Opens a folder in Finder. Relative paths resolve from the tool's directory. |
| Inline AppleScript | applescript |
Executes an AppleScript snippet. |
| AppleScript file | applescriptFile |
Runs a .applescript file. |
| Health check | healthCheck |
Runs a one-shot HTTP or TCP check and shows the result inline (latency on success, "failed" on failure). See config.json Reference → Health Checks. |
Port placeholders in URLs
Action URLs can include {{port}} and {{port:Label}} placeholders:
{{port}}: replaced with the detected port of the tool's last process (typically the user-facing one).{{port:Backend}}: replaced with the detected port of the specific process namedBackend.
{ "label": "Open Frontend", "type": "url", "url": "http://localhost:{{port}}/" }
{ "label": "API Docs", "type": "url", "url": "http://localhost:{{port:Backend}}/swagger" }
If the tool isn't running (no port detected yet), the placeholder stays literal and the link won't open.
Controlling when actions appear
For service tools, actions have a showWhen field:
"running"(default): only visible when the service is running."stopped": only visible when stopped or errored."always": visible either way.
showWhen is ignored on shortcut and group actions; those are always visible.
Per-card collapse
Each card has a chevron in the top-right that collapses its body. The collapsed state persists across popover reopens and app restarts (stored in ~/Library/Preferences/ca.bonevil.runyard.plist under the collapsed.{toolName} keys). The header Collapse all / Expand all toggle overwrites every card's stored state in one click.
Port detection
Runyard uses lsof to watch the PID tree of each process you spawn and picks up the first TCP port it starts listening on. The detected port:
- Shows up next to the tool name as a blue
:3001tag on the meta-line. - Gets auto-injected into
startupCheckURLs that don't include an explicit port (e.g.,http://localhost/api/healthbecomeshttp://localhost:3001/api/health). - Replaces
{{port}}placeholders in action URLs.
Fallback: if auto-detection doesn't find a port (rare, but some tools fork in unusual ways), set startupFallbackPort on the process.
Start, Stop, and Logs
Start
The Start button runs:
installCommandif the marker path (default:node_modules) doesn't exist.- Each
startCommandin order. Commands withwaitFor: "OtherLabel"pause until the other process passes its health check. - Health check polling (if configured).
The tool transitions through starting → running once all processes are healthy.
Stop
The Stop button either:
- Runs your custom
stopCommandsin order, then force-kills any leftovers. - Or, if no
stopCommandsare configured, sends SIGTERM → waits → sends SIGKILL.
View Logs
Every spawned process writes to its own log file under ~/Library/Logs/Runyard/. View Logs opens each log file in Console.app (or falls back to the directory if no log files exist yet). See Troubleshooting → Logs for the full path scheme.
Clearing errors
When a service fails to start or crashes, the card turns red and the menu bar icon shows a red warning triangle. The error message is shown inline below the meta-line, and the full reason is written to the tool's log file.
To dismiss the error indicator without restarting:
- Single service: click Clear Error in the card footer (or in the nested chip-row for a group child). The state returns to Stopped and the warning triangle disappears.
- Group with multiple errors: when two or more services in a group are errored, Clear Errors (N) surfaces as a red footer button on the group card and clears them all in one click.
Clearing only changes what's displayed; your log files are kept on disk at ~/Library/Logs/Runyard/.
Keep Awake
Runyard ships caffeinate integration to prevent macOS from sleeping while important work is running. Three independent paths, any combination of which can be active at the same time:
-
Manual: the row above the footer. Click it to switch on for your default duration (Settings → General → Keep Awake → Default duration; 1 hour out of the box). Click again to switch off.
While a timed session is active, the row fills with a left-to-right coffee gradient that recedes as the countdown drains. The state text on the right reads
45 min,23 min, … When you pick Until I disable it, the row stays fully filled and the state readsIndefinitewith a soft pulse.Click the ▾ chevron to open the duration menu: 15 / 30 / 60 / 120 minutes, or until-I-disable-it. The same menu has an Also prevent display sleep check (equivalent to
caffeinate -d). Toggling it mid-session replaces the runningcaffeinateso-dtakes effect immediately. -
Service-bound: any service with
keepSystemAwake: trueinconfig.jsonspawns onecaffeinate -i -w <pid>per running start-process. The system stays awake as long as any of the service's processes is alive (graceful exit, SIGKILL, or crash all release that process's assertion); the wake guarantee survives a short-lived init process exiting while the long-running ones continue. The service card shows a small ☕ in its meta-line while at least one session is live. -
Health-check-bound: a probe with the same flag holds an indefinite
caffeinate -iwhile it's polling. Pausing the probe from the popover (or removing it from the config) releases the assertion immediately.
When at least one source is active and no manual session is running, the toggle row swaps to a green active wash and reads Auto · Auto · <Name> +N for multiple). When manual and automatic sources are active together, the manual countdown wins the headline and the +N chip indicates additional automatic sources; hover the row to see the full list.
The menu-bar hover panel echoes the same summary as a ☕ 45 min · Auto · Big Build +2 line under the running/failing counts.
Lid-closed limitation: macOS does not keep your Mac awake when the lid is closed, regardless of which sleep-prevention tool you use. Runyard shows a one-time alert the first time you toggle Keep Awake on, to set the expectation correctly. This is a thermal-safety constraint and applies to any caffeinate-based tool.
Global actions (popover footer)
The four tiles at the bottom of the popover:
- Settings: opens the Settings window (General, Tools, Advanced, About, Purchases).
- Reload: re-parses
config.jsonand rebuilds the popover. Running tools are stopped first. - Updates: checks for app updates.
- Quit: stops all tools and exits. Any
caffeinatesubprocess Runyard owns is terminated as part of the shutdown.