AX.25 Terminal
Interactive AX.25 connected-mode sessions to BBSes, PMSes, and other live packet hosts — right in the browser
The AX.25 Terminal is a built-in connected-mode client. Open it, pick a radio channel, type the callsign of a BBS or another live station, and you get an xterm-style window into a live LAPB session over RF — no second program, no AGWPE bridge, no KISS-side TNC software. Graywolf speaks AX.25 v2.0 (modulo 8) and negotiates modulo 128 when the peer supports it, with automatic fallback if the peer answers SABME with DM.
Prerequisite: set the channel mode
Connected-mode AX.25 is only enabled on channels whose
Mode is set to packet or
aprs+packet. APRS-only channels refuse new sessions
— the pre-connect form on those channels switches to a
read-only raw-tail view instead (see Raw-tail mode
below). Set the mode on
Radio Channels.
The mode column gates transmission, not reception.
A packet channel still receives and demodulates
everything — APRS frames keep flowing into the live map and
packet log. The mode just refuses new outbound APRS beacons,
digipeats, and iGate IS→RF, so the channel can be dedicated
to a single LAPB conversation without other subsystems
stepping on it.
Opening a session
Click Terminal in the sidebar. With no session active, you get the pre-connect form:
| Channel | Pick the radio channel to transmit on. Only channels with a packet-capable mode appear. |
| Local Callsign | Your station, optionally with an SSID: K0SWE or K0SWE-3. SSID 0 through 15 is the AX.25 spec; pick a different SSID per concurrent session if you plan to run multiple links from the same callsign. |
| Destination Callsign | The BBS or peer station, same format. For a typical W1AW BBS that would be something like W1AW-4. |
| Via path | Optional comma-separated digipeater list (up to 8 hops). Leave blank for a direct connection. |
Click Connect. The form swaps for the live xterm viewport as soon as the link reaches CONNECTED state. If the peer answers DM (busy / not listening), the form re-appears with the reason in red.
Advanced timer settings
The pre-connect form has an Advanced disclosure that opens to AX.25 protocol knobs. Operators almost never need to touch these — the defaults match what a Linux kernel ax25 stack would use — but they are exposed for the cases that do.
| Field | Default | What it does |
|---|---|---|
mod 128 |
off | Negotiate modulo-128 sequence numbers (SABME). Lets the link have up to 127 outstanding I-frames instead of 7. Falls back to mod-8 automatically if the peer answers DM. |
paclen |
0 (server picks) | Maximum I-field bytes per outbound I-frame. 0 means use the kernel-default 256. |
window |
0 (server picks) | Outstanding-I-frame window. 0 means 4 (mod-8) or 32 (mod-128). |
N2 |
0 (server picks) | Maximum retransmits before declaring the link down. 0 means 10. |
T1 / T2 / T3 ms |
0 (server picks) | Retransmit / response-delay / link-idle timers. 0 leaves the kernel-default values in place; override only if your path RTT is unusual. |
backoff |
linear | Retry backoff curve: linear, exponential, or none. Exponential helps on noisy channels. |
Defaults you set on Preferences (modulo and paclen) seed the form; per-session changes are not persisted — the next connection starts from the Preferences defaults again.
While connected
The connected view shows a fixed 80×24 xterm window with a status bar across the bottom: state, peer, RTT, and a small radio button that toggles the telemetry side panel. The telemetry panel updates once per second while CONNECTED and shows the live LAPB counters: V(S), V(R), V(A), retry count (N2), busy / peer-busy flags, and the RTT exponential moving average. It stops updating in any other state.
Type to send. Anything you type goes out as I-frame data; bytes
from the peer come in and render in the viewport. The terminal
uses xterm.js with disableStdin: false, so your
keystrokes never echo locally — the BBS sees them, the BBS
echoes them back, and you see what the BBS chose to display. This
is the correct behavior for a real LAPB session: the peer is the
authority on what gets shown.
Command bar
Press Ctrl-] to drop into the command bar at the bottom of the terminal. From there:
| Command | Effect |
|---|---|
disconnect |
Send DISC, wait for UA, close the link gracefully. |
abort |
Send DM and close immediately. Use when the peer is unresponsive. |
transcript on / transcript off |
Start or stop recording the session to the transcript store. |
Esc closes the command bar without acting.
Macros
The macro toolbar above the viewport holds operator-defined buttons. Each macro has a label and a payload (text or arbitrary bytes); clicking the button sends the payload immediately as I-frame data. Edit macros from the gear icon — payloads are stored base64-encoded so they can hold control characters and non-printable bytes.
Macros are global (one set per station), not per-session.
Tabs
The tab bar above the macro toolbar lets you run up to six concurrent sessions. Each tab is its own LAPB link, so you can run a BBS session in one tab and a peer-to-peer keyboard-to-keyboard chat in another. The unread-bytes count in each tab’s label tracks bytes that arrived while the tab was not focused; the sidebar Terminal entry shows the same total across all backgrounded tabs.
Ctrl-PageUp and Ctrl-PageDown move between tabs from the keyboard.
Transcripts
Turning transcript on begins recording every byte
and state-change to the transcript store. The recorder logs RX
bytes, TX bytes, state transitions, and link errors; the
Transcripts sub-route lists all completed
sessions and lets you replay or delete them. Transcripts live in
the SQLite config DB and are not size-bounded, so prune
occasionally if you record a lot.
Transcripts capture the byte stream as the session saw it, including the BBS’s screen-clear sequences and form-feeds. Replays render in a styled monospace viewer rather than a live terminal — expect to see escape sequences as text, not as cursor moves.
Saved profiles and recents
Successful CONNECTED transitions are upserted into a recents list under the pre-connect form. Click any recent to populate the form with its channel, callsigns, via path, and timer settings. Pin a recent (the pin icon turns it into a saved profile) and it stays at the top of the list permanently — pinned profiles never get trimmed; unpinned recents top out at 20 entries.
Raw-tail mode (APRS-only channels)
If you open the Terminal page on an APRS-only channel, the
pre-connect form is replaced with a raw-packet viewer instead.
It subscribes to the same packet-log fan-out the
Monitoring page uses and shows
every RX and TX frame on that channel as it happens, in TNC2
text format. There is no LAPB session in this mode — the
raw-tail view is for watching APRS traffic live; switch the
channel mode to packet or aprs+packet
first if you want to open a session.
Defaults and theming
Preferences has an AX.25 Terminal tab where you can set:
| Scrollback rows | How much history the terminal viewport keeps. Larger values use more browser memory. |
| Cursor blink | On / off. Off is easier on the eyes for long sessions. |
| Default modulo | Pre-fills the Advanced → mod 128 checkbox on every new session. |
| Default paclen | Pre-fills the Advanced → paclen field. |
| Theme preset | classic, phosphor-green, or phosphor-amber. The terminal palette is resolved from CSS variables, so the preset also tracks the global graywolf theme. |
Out-of-scope
The terminal is an outbound-only client. It does not:
- Listen for inbound SABM and act as a BBS host.
- Implement NET/ROM, ROSE, or other layer-3 routing.
- Segment its own application-layer messages — what you type goes out as a single I-frame stream, paclen-fragmented at the LAPB layer only.
For a hosting-side BBS or a NET/ROM node, run a dedicated package (LinBPQ, JNOS, etc.) alongside graywolf and connect to it through the KISS interface.