Ctrl+C to Step Away: Keyboard Shortcuts for Phosphor's Session Persistence

August 31, 2026

Last week we shipped session persistence for Phosphor BBS: detach your session, close your terminal, reconnect later, and your screen, cursor, and half-finished input are exactly where you left them. The first post covered the detach/reattach architecture. This post is about the keyboard shortcuts we built to make it feel right.

Because the hardest part of session persistence isn’t keeping the state alive — it’s making the exit feel natural.

The key binding problem

A BBS is not a shell. When you SSH into a Linux box and hit Ctrl+C, you’re sending SIGINT to the foreground process. When you hit ESC in vim, you’re entering normal mode. These are deep muscle memories, and every terminal application has to decide how to honor or override them.

Phosphor has two exit paths with very different semantics:

  • Go back. You’re in a sub-screen — chat, file areas, a door game — and you want to return to the main menu. This is navigation, not disconnection. Your session stays live and attached.
  • Step away. You’re done for now. Close the laptop, lose signal, or just need to leave. Your session should detach — preserved on the server so you can pick it up later from any device.

Those are different actions. They deserve different keys.

What the keys do

Here’s the full key binding map for navigating and leaving Phosphor:

KeyWhere it worksWhat it does
ESCAny screenGo back one level — close the current screen and return to the parent. In a dialog, close the dialog. In a sub-screen, return to the main menu. On the main menu, disconnect and log out (session destroyed).
QAny screenSame as ESC — go back one level.
Ctrl+CAny screenDetach immediately — preserve your session and disconnect. Works from every screen, every dialog, every submenu. Your session is kept alive on the server for re-attach.
DMain menu onlyDetach session (same as Ctrl+C, but only from the main menu).

The important distinction: ESC goes back. Ctrl+C steps away.

On the main menu, ESC and Q perform a full disconnect and logout — the session is destroyed, just like logging out of any system. This is the “I’m done for the day” exit.

Ctrl+C, from anywhere, detaches. You were reading a message board three levels deep? Ctrl+C detaches. You were composing a chat message? Ctrl+C detaches. You were in the sysop control panel? Ctrl+C detaches. The session is frozen exactly as-is, and when you reconnect, you land right back where you were.

Why Ctrl+C means detach, not interrupt

In a traditional terminal, Ctrl+C sends SIGINT. In Phosphor, there is no foreground process to interrupt — the BBS is a structured application with screens, windows, and widgets, not a command line. The “interrupt what I’m doing” muscle memory maps cleanly to “interrupt my connection, but keep my work.”

The alternative — having Ctrl+C go back one screen like ESC — would waste the most instinctive exit key in the terminal world on a navigation action that already has two keys (ESC and Q). And making Ctrl+C a hard quit (destroy the session) would mean the most accessible exit key throws away your work. Neither option respects the session persistence model.

Ctrl+C as universal detach means: you can always leave quickly, and you never lose your place.

The implementation: one shortcut, every screen

The key handling lives in ContentScreen, the base class for every interactive screen in Phosphor. When a Ctrl+C keystroke arrives:

  1. ContentScreen.handleKeyStroke() detects Ctrl+C and calls onCtrlC()
  2. The default onCtrlC() calls session.detach()
  3. BbsSession.detach() snapshots the current screen state, marks the session as detached, evicts it from the active registry (so the Who’s Online list updates), moves it to the detached session index (so the sysop monitor still sees it as “Disconnected”), flushes and closes the terminal streams, and stops the GUI event loop

Individual screens can override onCtrlC() if they need cleanup before detaching — MainMenu, for example, unregisters its live listeners first. But the contract is the same: Ctrl+C always ends in a detach, not a navigation step.

ESC on the main menu: the clean exit

From the main menu, pressing ESC or Q calls disconnect() — a full logout. The session is marked closed, the persistent session record is cleared, and the server releases all state. This is the “I’m done” exit. The session won’t survive for re-attach because you meant to leave.

This is different from Ctrl+C on the main menu, which calls detach() — the session survives.

Two keys, two intentions, same screen. That’s the design.

The sysop monitor sees everything

A detail that matters for community operators: when a user detaches with Ctrl+C, they disappear from the Who’s Online list (they’re not “online” — they stepped away) but they stay visible in the SysOp Control Panel with a “Disconnected” status. The sysop can see who’s parked, who’s active, and how long someone’s been away. Re-attach flips the status back to “Connected” in real time.

This required fixing a subtle event-ordering bug: BbsSession.detach() fires sessionUnregistered before sessionDetached, and the table model has to handle both events without dropping the row in between. The fix — keeping detached sessions in the table through both events — means the sysop monitor never has a gap where a session vanishes and then reappears on refresh. It’s live and accurate.

Try it

Connect to bbs.phosphorbbs.net via SSH (port 2222) or the web terminal. Navigate somewhere interesting — open a message board, load a stock chart, start a chat. Then hit Ctrl+C. Close the terminal. Come back from a different device. Log in. Your screen is waiting.

That’s what a social terminal does: it holds your place.

Your community. Your terminal. Your rules.

Follow #dtp (#digitalthirdplace #socialterminal) for what ships next.