- Docs
- Implementation notes, by version
v0.5.5: verified notification transport
v0.5.4 could repeat an alert but could not say whether any alert reaches the person. This release adds a test the person asks for and a confirmation only the person can give. The policy is hooks/transport.ts (pure); hooks/register.tsx owns the host calls and the two commands.
States#
NOT TESTED → ATTEMPTED (the host did not report writing it) or SENT (the host named the channel it wrote to) → USER CONFIRMED (the person typed /cockpit guard seen). There is no delivered state. A confirmation is a person's statement in one session; it is not proof that a later alert will be seen.
Design#
- Apart from alerts. Tests live in their own session-state key (
guard-transport), never in the Run Ledger's alert records, and never read or write the alert state (guard-state). So they cannot spend the alert budget, start a cooldown, acknowledge an alert, or be mistaken for an incident. - The code. The notification carries four characters from an unambiguous alphabet, drawn from the host's web crypto (the clock if it has none). It is shown in the notification and nowhere else, not in any command output, so typing it back means the text was read. The code is required whenever the channel shows text; only a bell-only channel (
terminal_bell) is confirmed without one, kept asno-code. - Scope. Each test holds the session id and a connection kind:
sshwhenSSH_CONNECTIONorSSH_TTYis set for the process, elselocal. Only set-or-not is kept. Counting, status and confirmation use only this session's tests; a confirmation made on another connection kind is shown as not applying. - Limits. One test per 30 s and six per session, one at a time; a confirmation within 10 minutes; a confirmation counts for 12 hours; eight tests held.
/clearor a resume starts a new session id and so a new count. A clock that goes backwards waits or expires rather than passing. - Sound. Requested with
test sound, or implied byguardSound. It reusessound()and is not waited for; the test readspendinguntil a player answers, thensound,bellornone; apendingolder than a minute is shown as outcome unknown. - Authority.
testandseenare refused unless typed by a person (composer or bridge), likeackandsilence.
What was investigated, and what was not observed#
From the host's own declarations (Claude Code 2.1.296, UiNotifyResult):
- The channels are
iterm2,iterm2_with_bell,kitty,ghosttyandterminal_bell.terminal_bellrings and shows neither text nor title. - When nothing is sent the reason is
disabled,no-channel(the automatic setting found no terminal that notifies),no-surfaceorrefused. isSent: trueis the host having written the sequence or bell. A terminal that drops it, or a system that mutes banners, still reads true.
Consequences, stated as expectations and not as observations:
- Fedora KDE (Konsole), native. Konsole is not one of the host's notifying terminals, so the expectation is
no-channelunder the automatic setting and the bell withpreferredNotifChannel: terminal_bell. What Konsole does on a bell is its own profile setting. Not observed. - Claude Code in Boat over SSH. The host writes to the remote terminal stream; the sequence or bell is carried to the local terminal, which decides. The alert sound plays on the Boat machine: an audio player that exits 0 there is recorded
soundalthough the operator cannot hear it, and the bell (the chain's last entry, which is what could reach the operator) is tried only when no audio player answers. Not observed. - Disconnect and reconnect. The process, its environment and the session id do not change when a connection is re-established to a surviving session, so a reconnect cannot be detected. A notification written while disconnected is lost or buffered by whatever holds the session. The output tells the operator to test again after reconnecting. Not observed.
No KDE popup process, service, tunnel or credential was added.
Operator checklist#
Run each of these where you actually work. Type the commands yourself.
Fedora, native terminal:
/cockpit guard— expectTRANSPORT: NOT TESTED./cockpit guard test sound— note the line it prints (SENT via …, orATTEMPTED … no-channel).- If you see a popup with a code:
/cockpit guard seen <code>. If you only hear or see a bell:/cockpit guard seen. If nothing happens, do not confirm. - If it said
no-channel, set Claude Code'spreferredNotifChanneltoterminal_bell, restart, and repeat from step 2. Check your terminal profile's bell setting. /cockpit doctor— the Cobalt Guard section should showTRANSPORT: USER CONFIRMEDand no transport warning.
Boat over SSH:
- Repeat steps 1–3 inside the SSH session. Expect
connection: SSH. A recordedsound attemptedmeans a player ran on Boat, not at your desk, so judge only the popup or bell you actually get. - Disconnect, reconnect to the same session, and run
/cockpit guard testagain. An earlier confirmation still shows; treat only the new test as evidence. - With the terminal unfocused or minimised, run one more test: this is the case a real alert meets.
Then, with guardAlerts and guardRepeat on, block a task once and check that the real alert and its reminder arrive the same way.
Tests#
tests/transport.test.ts: code and message shape; the four states; rate limit; confirmation rules (none, not sent, wrong or malformed code, expired, backwards clock, other session, other connection kind, duplicate, older code); diagnostics wording; malformed stored state; the command end to end; person-only; sound not requested, played, bell, none, muted, off, failing, hanging; new session; hot reload; SSH; independence from the alert budget, cooldown, records, gates and reminders.
Independent review#
A separate reviewer approved the complete diff with three non-blocking findings, corrected before merge: the README stated terminal behaviour as fact where it is expectation; a confirmation never aged; and a read failure printed a fifth label. Its notes were also taken: the code is now required except on a bell-only channel, an empty session id is refused everywhere, a stale pending sound is reported as unknown, tests cannot overlap, and a failing clock cannot throw out of the command. A second reviewer then read the complete corrected diff and approved it, with two documentation corrections (a leftover v0.5.4 sentence about the bell, and this paragraph) and a request for an iterm2_with_bell test; those were applied and that last delta was reviewed as well.
No automated test here shows that a person saw or heard anything. Real-terminal acceptance is NOT VERIFIED until the checklist is run.