⌄
The settings window as four toolbar panes, plus the hub status light in the jumplist footer. The window title tracks the pane.
ctrl+alt+j config syntax;
clearing it leaves the jumplist reachable only from the menu bar; and the board filter field.
The recorder and filter each have a one-line caption.Every mode configures the hub on this machine. That never changes, and the captions say so:
The sketches sit below the caption. Every one is built from the same labelled unit: the small boxes are agents and they sit inside a machine frame, with the hub as the dot they report to. One rule carries the rest: the filled dot is the board of record. In Local and LAN it sits on this machine. In Remote each machine's dot goes hollow and the filled one moves to the cloud that they all forward to. Dashed links cross a network, solid links do not.
Picking a different mode in the popup changes nothing by itself. The caption, sketch and controls switch to the new mode so you can see what you are choosing, a Confirm and a Cancel button appear beside the popup with the hint under it, and the status pill keeps showing what is actually running. The buttons sit in the popup's row because that is the control that caused the pending change; at the foot of the pane they read as a form submit for everything above them. Confirm writes the settings file and restarts the hub; Cancel puts the popup back to the running mode. This is the standard idiom for a change that interrupts a running service, and it means the window can never half-apply a mode.
hub.remoteUrl, which no mode switch clears) and the token field with
hub.token; only hub.upstream empties when Local or LAN is confirmed,
because that key alone is what makes the hub a forwarder. Fill in the remote hub's URL and
token, then Test. Confirm stays disabled
until a test passes, so a forwarder is never pointed at an address that has not answered. The
test calls the remote hub's /healthz and checks the token against an authenticated
route; failures render under the field with the actual reason.While a change is pending, that mode's own controls are visible but anything that needs the hub to already be in the mode is disabled: Pair a device... greys out under a pending LAN selection, because pairing hands a device the running hub's LAN address and bearer and the hub is still on loopback until Confirm. Its tooltip says why.
One line of reality directly under the mode control: a dot, the running mode, and where the
hub is, with a short amber suffix when something is wrong. It renders
GET /healthz and nothing else - the settings file records what you asked for and is
never read to describe what is running. During a pending change or a restart the pill keeps
telling the truth about the running hub. The address is monospace and selectable, so it can be
copied straight out of the pill. The pill never narrates health in sentences and never counts
anything; the popover and the log carry the detail. When the hub does not answer at all, the pill
still has to name a mode, so it uses the last mode the hub reported this session, or the settings
file as a last resort - intent never describes a hub that IS answering.
Karabiner-Elements and iTerm2 both put logging in a settings tab, and both are apps whose real
work happens in a process the user never sees. Signalbox has the same shape: the app spawns
signalbox hub as a child and its output lands in
~/.local/state/signalbox/hub.log. When a board is empty or a phone will not pair,
that file is the answer.
The tab is deliberately small. The path is clickable and reveals the file in Finder; Copy path
puts it on the clipboard. Recent events is a tail of the last 500 lines in a scrolling monospace
view, read when the tab opens and on Refresh - not a live follow, so nothing moves while you
read. The tail uses system text colors and follows the light or dark appearance, like Console.app,
and it opens scrolled to the newest lines with the start of the line visible, never mid-line. It is
a tail, not a viewer: no search, no level filter. Anything deeper is a job for Console or
tail -f, and the path is right there.
What this asks of the hub: log lines gain a leading YYYY-MM-DD HH:MM:SS
timestamp. Bare untimestamped lines make a tail nearly useless, so that is a required change, not
a nice-to-have.
Everyday status does not live in Settings - it is a small light at the left of the jumplist footer, opposite the cog. Three states: Hub healthy (green), Hub warning (amber), Hub starting (grey). The light never names a mode - "Remote" as a status reads as a place, not a health.
Clicking the light opens a popover of facts, not narrative: Mode, Address, Hub version, and in Remote mode the Uplink row with the spool count when events are queuing. Values are monospace and selectable. Two links close it out: Open Settings (the Hub tab) and Open log (the Logs tab). Clicking elsewhere closes it. There are no menu bar menu items for any of this.
| Store | What | Why |
|---|---|---|
UserDefaults | menu bar icon (menuBarIcon), jumplist shortcut (KeyboardShortcuts_openJumplist, the live value written by the recorder: a JSON-encoded shortcut or boolean false when deliberately cleared; legacy hotkey, migration only: the old ctrl+alt+j config string, read once at first launch to seed the key above, then ignored and left in place; defaults write no longer changes anything), board tag filter (tagFilter), jumplist zoom (uiScale), login-item one-shot (didRegisterLoginItem) | only the app needs these |
~/.config/signalbox/settings.json | toggles the CLI hooks must also see (claudeClearEnds, claudeRenameTitle, codexClearEnds, codexRenameTitle); plus the hub keys Confirm writes: hub.bind (127.0.0.1 for Local, 0.0.0.0 for LAN; keywords loopback/lan still resolve), hub.token (bearer token; auto-minted on a wide bind; the Remote token field also writes it), hub.upstream (the remote hub URL for Remote mode; empty otherwise), hub.remoteUrl (the same address, remembered: no mode switch clears it, so Remote comes back with its URL already filled in). These keys are intent - what is actually running is whatever /healthz says. | the app writes the file and the hook path reads it on every fire; the hub reads the hub keys at start so the app-spawned hub needs no flags |
Writes merge key by key, so unknown keys survive. A missing or broken file falls back to defaults and keeps the hook path working.
Claude Code fires SessionEnd with reason clear on /clear.
Checked (default), the session ends and leaves the board. Unchecked, the hook maps it to
done so the old session stays listed until you hide it (⌃X), remove it
(⌃⌫), or it expires. The Codex pair behaves the same. For scripting,
SIGNALBOX_CLEAR_ENDS=0|1 overrides the file. Rename toggles: on, the agent's /rename
shows on the board as the session name; off keeps the folder name; your own ⌃R rename always
wins.
| String | Where | When it shows |
|---|---|---|
| Press the field to record a global shortcut | General tab, under the shortcut recorder | Always |
| Always applied on the jumplist | General tab, under the filter field | Always |
| Record Shortcut | General tab, shortcut recorder | When no shortcut is recorded |
| Press Shortcut | General tab, shortcut recorder | While recording a shortcut |
| Remove the session on /clear | Agents tab, both agent sections | Always; the session leaves the board when checked |
| Rename the session on /rename | Agents tab, both agent sections | Always |
| Local - 127.0.0.1:8377 | Status pill, green dot | Running mode is Local and the hub answers |
| LAN - 192.168.1.24:8377 | Status pill, green dot | Running mode is LAN and the hub answers |
| LAN - port 8377, no Wi-Fi address yet | Status pill, green dot | Running mode is LAN and this Mac has no LAN IPv4 yet |
| Remote - signalbox-hub.fly.dev | Status pill, green dot | Running mode is Remote, the hub answers and the uplink is up |
| Local - 127.0.0.1:8377 - not responding | Status pill, amber dot | Running mode is Local and the hub does not answer |
| LAN - 192.168.1.24:8377 - not responding | Status pill, amber dot | Running mode is LAN and the hub does not answer |
| Remote - signalbox-hub.fly.dev - uplink down | Status pill, amber dot | Running mode is Remote, the local hub answers and the uplink does not |
| Local - 127.0.0.1:8377 - starting | Status pill, grey dot | The hub is not answering yet, on launch or after Confirm |
| The hub runs on this machine and only this machine can reach it. | Hub tab, mode caption | Local is selected |
| The hub runs on this machine and is open on your local network so your phone can connect to it. | Hub tab, mode caption | LAN is selected |
| The hub on your machine forwards to a remote hub. A remote hub can accept forwards from many machines at once. | Hub tab, mode caption | Remote is selected |
| Pair a device... | Hub tab, LAN control | LAN is selected; disabled while the LAN selection is still pending |
| Confirm LAN first. Pairing needs the hub already running in LAN mode. | Tooltip on a disabled Pair a device... | On hover, while a change to LAN is pending |
| Remote hub URL | Hub tab, Remote field label | Remote is selected |
| Token | Hub tab, Remote field label | Remote is selected |
| Test | Hub tab, Remote control | Remote is selected |
| Testing the address. | Hub tab, under the fields | While a test is in flight |
| Reached the hub. It is running signalbox 0.9.2. | Hub tab, green | The test got a healthy, authenticated answer |
| No answer from that address. | Hub tab, amber | The test got no answer, or an answer that was not a signalbox hub |
| The hub rejected the token. | Hub tab, amber | The test reached a hub but the token failed |
| Confirm | Hub tab, beside the mode popup, primary | A mode change is pending; disabled for Remote until a test passes |
| Cancel | Hub tab, beside the mode popup | A mode change is pending |
| Confirming restarts the hub. | Hub tab, under the mode row | A mode change is pending |
| Test the address before confirming. | Hub tab, under the mode row | Pending change to Remote, no passing test yet |
| Hub healthy | Footer light, green dot | The hub answers; in Remote, the uplink is also up |
| Hub warning | Footer light, amber dot | The hub does not answer, or the uplink is down in Remote |
| Hub starting | Footer light, grey dot | The hub is not answering yet, on launch or after Confirm |
| Hub status. Click for details. | Tooltip on the footer light | On hover |
| Mode / Address / Hub version / Uplink | Popover row labels | Uplink only in Remote |
| not answering | Popover, Address value, amber | The hub does not answer |
| connected | Popover, Uplink value | Remote, uplink up |
| down, 12 events spooled | Popover, Uplink value, amber | Remote, uplink down (count from the spool) |
| Open Settings / Open log | Popover links | Always |
| The hub writes everything it does here. Click the path to reveal it in Finder. | Logs tab caption | Always |
| Recent events | Logs tab, tail heading | Always; the tail holds the last 500 lines |
| Copy path | Logs tab | Always |
| Copied the path. | Logs tab, under the path | For a few seconds after Copy path |
| Revealed hub.log in Finder. | Logs tab, under the path | For a few seconds after clicking the path |