Send interactive messages and toast notifications to users inside a running booth.
booth message lets you communicate with users inside running booths — show dialogs that require a response, or fire-and-forget toast notifications that auto-dismiss. It uses the booth UI overlay to render messages on top of the booth's web UI.
# Ask a yes/no question (blocks until user responds)
booth message send --name my-booth --title "Deploy?" --body "Deploy to staging?" --type yes-no
# Show a toast notification (returns immediately)
booth message send --name my-booth --title "Build done" --body "Build completed successfully." --type toastBack to README
Booth messaging provides a way for scripts, CI pipelines, or the host CLI to present interactive UI to users working inside a booth. Messages appear as overlay dialogs on top of the booth's web UI (terminal, VS Code, Jupyter, or desktop).
All variants use the shared booth UI overlay — an nginx wrapper that injects an HTML overlay into the page. This means messages look and behave identically across all variants.
Send a message and (for interactive types) wait for a response.
booth message send [flags]| Flag | Description | Required |
|---|---|---|
--title |
Message title | Yes |
--body |
Message body text | Yes |
--type |
Message type (see Message Types) | No (default: yes-no) |
--name |
Target booth name (default: current directory name) | No |
--options |
Comma-separated options (for choice, choice-text, radio, checkbox) |
Required for those types |
--expires |
Timeout duration (e.g., 5m, 1h). Default: 10m for interactive, 30s for toast |
No |
--id |
Custom message ID (must match [A-Za-z0-9._-]+). Lets you target the message later with booth message adjust. |
No (default: auto-generated) |
Output: Prints the message ID on the first line. For interactive types, prints the user's answer on the second line after they respond (or timeout if expired). For toast and banner, the CLI returns immediately after writing the message file.
Update the title and/or body of an existing message file. Useful for banner messages whose content evolves over time (build status, progress, etc.).
booth message adjust [--name <booth>] <msg-id> [--title <new>] [--body <new>]| Flag | Description | Required |
|---|---|---|
--title |
New title (omit to keep existing) | No |
--body |
New body (omit to keep existing) | No |
--name |
Target booth name (default: current directory name) | No |
At least one of --title or --body must be provided. The overlay reconciles changes on its next poll (≤ 2s).
# Send a banner with a deterministic id, then update it as the work progresses
booth message send --type banner --id ci-status --title "CI" --body "Step 1 of 5"
booth message adjust ci-status --body "Step 3 of 5"
booth message adjust ci-status --title "CI complete" --body "All green."If the user dismisses the banner (clicks OK) before you adjust, the overlay won't re-show it — adjust only patches the live state, it doesn't re-publish a dismissed message.
List all messages (pending and answered) for a booth.
booth message list [--name <booth>]Read the response for a specific message.
booth message response [--name <booth>] <msg-id>| Type | Buttons / Input | User Response | CLI Behavior |
|---|---|---|---|
yes-no |
Yes, No | yes or no |
Blocks until response |
yes-no-cancel |
Yes, No, Cancel | yes, no, or cancel |
Blocks until response |
ok |
OK | ok |
Blocks until response |
text |
Text input + Send | Free-form text | Blocks until response |
password |
Password input + Send | Free-form text (masked) | Blocks until response |
choice |
One button per option | Selected option text | Blocks until response |
choice-text |
Option buttons + text input | Selected option or typed text | Blocks until response |
radio |
Radio buttons + Submit | Selected option (single) | Blocks until response |
checkbox |
Checkboxes + Submit | Comma-separated selections | Blocks until response |
toast |
None (click to dismiss) | dismissed (auto) |
Returns immediately |
banner |
OK button | ok (when user clicks) |
Returns immediately |
All interactive types show a centered modal overlay with a semi-transparent backdrop. The iframe (terminal, IDE, desktop) is blocked from interaction while the overlay is visible.
Toast notifications appear in the bottom-right corner without blocking interaction. Banner notifications appear at the top-center, also without blocking interaction, and stay until the user explicitly clicks OK.
Note: choice, choice-text, radio, and checkbox all require the --options flag.
Sets how long a message remains active before timing out.
# Interactive message with 5 minute timeout
booth message send --title "Continue?" --body "Proceed with migration?" --type yes-no --expires 5m
# Toast that stays for 1 minute
booth message send --title "Heads up" --body "Maintenance in 10 minutes" --type toast --expires 1m- Interactive messages: Default 10 minutes. If the user doesn't respond, the CLI exits with answer
timeout. - Toast: Default 30 seconds. The toast auto-dismisses when the timer expires.
Target a specific booth by name. If omitted, defaults to the current directory name (matching standard booth resolution).
booth message send --name my-project --title "Hello" --body "World" --type okToasts are non-blocking notifications that appear in the bottom-right corner of the booth UI.
- Stackable — Multiple toasts stack vertically (newest at bottom).
- Auto-dismiss — Each toast has a countdown progress bar and auto-dismisses when time expires (default: 30s).
- Click to dismiss — Users can click a toast to dismiss it early.
- Fire-and-forget — The CLI returns immediately after writing the message file. No response is expected.
- No iframe blocking — The user can continue working while toasts are visible.
# Quick notification (30s default)
booth message send --type toast --title "Build" --body "Build completed successfully."
# Longer notification
booth message send --type toast --title "Warning" --body "Disk usage above 80%" --expires 1m
# Stack multiple toasts
booth message send --type toast --title "Step 1" --body "Downloading dependencies..."
booth message send --type toast --title "Step 2" --body "Compiling source..."
booth message send --type toast --title "Step 3" --body "Running tests..."Banners are non-blocking notifications that pin to the top-center of the booth UI. Unlike toasts they do not auto-dismiss — they stay until the user clicks OK or the host removes the message file.
- Top-centered — Stacked vertically, above the iframe.
- Persistent — No timer; the user dismisses explicitly.
- No iframe blocking — The user can keep working while a banner is visible.
- Updatable — Pair
--id <handle>onsendwithbooth message adjust <handle>to change the title/body live. - Fire-and-forget — The CLI returns immediately after writing the message file, the same as toasts.
# A simple banner
booth message send --type banner --title "Maintenance" --body "Disk repacking; expect slower I/O for ~10 min."
# Live-updating banner driven by a build pipeline
booth message send --type banner --id build --title "Build" --body "Compiling…"
make build && booth message adjust build --body "Tests…"
make test && booth message adjust build --title "Build complete" --body "All green."When using the terminal variant (booth --variant terminal) or a shell session (booth -- bash, booth shell), there is no browser UI. Messages are handled via a prompt notification and the booth--msg command inside the container.
Before each prompt, a hook checks for pending messages and prints a notification:
[booth] 2 pending message(s) — run booth--msg to respond
coder@booth:~/code$
This is non-intrusive — it only appears between commands, never mid-typing.
Run inside the container to view and respond to messages interactively:
booth--msg # Show and respond to pending messages one by one
booth--msg list # List all messages (pending + answered)
booth--msg dismiss # Dismiss all pending toastsEach message type has a terminal-appropriate interaction:
| Type | Terminal Interaction |
|---|---|
ok |
Press ENTER to acknowledge |
yes-no |
Type y or n |
yes-no-cancel |
Type y, n, or c |
text |
Type free-form text |
password |
Type text (input hidden) |
choice |
Inline arrow-key menu |
choice-text |
Inline arrow-key menu + "Type my own" option |
radio |
Inline arrow-key menu |
checkbox |
Inline checklist (arrow keys + SPACE to toggle) |
toast |
Auto-dismissed (shown briefly then acknowledged) |
banner |
Press ENTER to acknowledge (terminal can't render the floating bar) |
Booth messaging uses the booth UI overlay infrastructure. See that document for details on the nginx wrapper, iframe embedding, and API server architecture.
- CLI writes a
.msg.jsonfile to.booth/.tmp/messages/on the host (bind-mounted into the container). - Overlay JS polls the API server every 2 seconds for pending messages.
- Overlay renders the appropriate UI — modal dialog (interactive types) or toast notification.
- User responds (clicks a button, submits text, or toast auto-dismisses).
- Overlay POSTs the answer to the API server, which writes a
.response.jsonfile. - CLI detects the response file and prints the answer (interactive types only).
Message file (<id>.msg.json):
{
"id": "msg-1234567890",
"title": "Deploy?",
"body": "Deploy to staging?",
"type": "yes-no",
"created": "2026-04-06T12:00:00Z",
"expires": "2026-04-06T12:10:00Z"
}Response file (<id>.response.json):
{
"id": "msg-1234567890",
"answer": "yes",
"answered": "2026-04-06T12:00:15Z"
}Files are stored in .booth/.tmp/messages/ and cleaned up automatically when the booth restarts.
# Ask for confirmation before a destructive action
answer=$(booth message send --name dev-booth --title "Drop table?" \
--body "This will delete all data in the users table." \
--type yes-no --expires 1m | tail -1)
if [ "$answer" = "yes" ]; then
echo "Dropping table..."
else
echo "Cancelled."
fianswer=$(booth message send --name dev-booth --title "Environment" \
--body "Select deployment target:" \
--type choice --options "staging,production,rollback" \
--expires 2m | tail -1)
echo "Selected: $answer"# User can click a preset option or type their own
answer=$(booth message send --name dev-booth --title "Branch" \
--body "Select or type a branch:" \
--type choice-text --options "main,develop,release" \
--expires 2m | tail -1)
echo "Branch: $answer"answer=$(booth message send --name dev-booth --title "Log Level" \
--body "Select log level:" \
--type radio --options "debug,info,warn,error" \
--expires 2m | tail -1)
echo "Level: $answer"# Answer is comma-separated, e.g. "logging,metrics,tracing"
# Returns "none" if nothing selected
answer=$(booth message send --name dev-booth --title "Features" \
--body "Select features to enable:" \
--type checkbox --options "logging,metrics,tracing,profiling" \
--expires 2m | tail -1)
echo "Enabled: $answer"secret=$(booth message send --name dev-booth --title "Auth Required" \
--body "Enter your deploy key:" \
--type password --expires 5m | tail -1)booth message send --type toast --title "Build started" --body "Compiling project..."
make build
booth message send --type toast --title "Build complete" --body "Ready to test."