Browser-side IBM 3270 terminal, plus a 5250 client for IBM i / AS/400 (beta). No backend, no server-side code, no runtime build step - just static files served over HTTP. The datastreams and TN3270E / TN5250E telnet negotiation are implemented in plain JavaScript modules running in the page; the page connects to the host through any websockify-style TCP↔WebSocket relay.
You need three things: this repo, a static HTTP server, and a WebSocket-to-TCP bridge that forwards binary frames to your host.
cd public
python3 -m http.server 8080
# → open http://localhost:8080/The index page lets you pick the protocol - 1 opens the TN3270
client (/tn3270/), 2 opens the TN5250 client (/tn5250/). Any
static server works; nothing in the project requires a build step.
websockify is the simplest
option. To expose a Hercules MVS turnkey listening on localhost:3270
(for TN3270):
websockify 0.0.0.0:6080 localhost:3270Or to point the TN5250 client at the public IBM i at pub400.com:
websockify 0.0.0.0:6080 pub400.com:23For multi-target routing, websockify also supports token-based target
configuration. The terminal sends binary as the WebSocket subprotocol;
websockify accepts it by default.
Or skip running your own bridge - two public test instances are already up:
wss://tk5.bencz.cc/ (Hercules MVS 3.8j Turnkey 5 - for TN3270)
wss://pub400.bencz.cc/ (pub400.com IBM i - for TN5250)
The MVS image is from tk5-hercules; the IBM i side relays to pub400.com, where you can register a free user profile. Drop the URL into the bridge field and connect - no setup needed. TLS is terminated at the bridge, so the page works fine when served over HTTPS. For testing only; don't use real credentials.
In the toolbar, set the bridge URL - for example one of:
ws://localhost:6080/ (single backend)
wss://tk5.bencz.cc/ (public test server)
wss://relay.example.com/tcp?port={port} ({port} is substituted)
Pick the model (3278-2 default), press Connect, and the OIA at the bottom turns green.
Telnet / TN3270E (RFC 2355):
- BINARY, EOR, TERMINAL-TYPE option negotiation (RFC 1041)
- TN3270E DEVICE-TYPE / FUNCTIONS subnegotiation; only the implemented
RESPONSESfunction is advertised - 5-byte data-stream header on inbound and outbound records
- Outbound sequence numbers are unique and monotonically increasing (RFC 2355 §3.2)
- Dispatch by data-type - only
3270-DATAis fed to the parser; control data types are not falsely advertised as supported functions - ALWAYS-RESPONSE / ERROR-RESPONSE handled correctly:
positive
RESPONSEon success, negativeRESPONSE(with sense byte) when the parser rejects a record, both echoing the host's seq. - IAC NOP keepalive after 120 s idle.
3270 datastream:
- Commands:
W,EW,EWA,EAU,WSF,RB,RM,RMA(both EBCDIC- and CCW-encoded forms accepted) - Orders:
SBA,SF,SFE,SA,MF,IC,PT,RA,EUA,GE - WCC bits:
RESET-MDT,RESTORE-KEYBOARD,SOUND-ALARM,RESET-PARTITION,START-PRINTER - 12-bit and 14-bit buffer addresses (auto-detected on input, always 12-bit on output)
- Cyclic field model - fields that wrap around the end of the buffer
- Field MDT tracking; modified-field replies use
f.start + 1 - Validation attributes (mandatory-fill / mandatory-entry) - terminal refuses the AID and parks the cursor on the offending field, just like a physical 3278.
- Set Reply Mode SF (0x09) is honoured - Read Buffer responses adapt to field / extended-field / character mode with the requested attribute list.
Query Reply (sent in response to Read Partition Query):
Summary, Usable Area, Character Sets, Color, Highlight, Reply Modes, Implicit Partition.
File transfer - IND$FILE:
- Download (host → browser): user types
IND$FILE GET dataseton TSO/CMS; the terminal collects the WSF data records (rectype 0x47, subtype 0x04), acknowledges each with the running buffer number, and on CLOSE triggers a browser download with the dataset name. - Upload (browser → host): click Upload… in the toolbar,
pick a local file, then run
IND$FILE PUT dataseton TSO. The queued bytes are streamed back in 2 KB chunks in response to the host's 0x46 0x11 requests, terminated with an EOF error record. - Works for both
FT:DATA(binary/ASCII files) andFT:MSG(host status messages, surfaced as a flash status). - Compressed mode (
IND$FILE GET ... COMP) is detected and refused with a clear message - use the default uncompressed transfer.
Models supported (3270):
| Model | Rows × Cols | TerminalType |
|---|---|---|
| 2 | 24 × 80 | IBM-3278-2-E |
| 3 | 32 × 80 | IBM-3278-3-E |
| 4 | 43 × 80 | IBM-3278-4-E |
| 5 | 27 × 132 | IBM-3278-5-E |
UI:
- Canvas-rendered display with extended color (3279-class palette), highlighting (underscore, reverse video, intensify)
- OIA status bar - connection LED, keyboard lock, insert mode, alarm flash, terminal type, cursor R/C
- Insert-mode toggle (Insert key) with overflow alarm
- Mouse selection, copy / paste / select-all (⌘/Ctrl+C/V/A)
- Rule cross-hair (toggle in toolbar)
- NVT overlay for ASCII banners before BINARY is negotiated
- Connection profiles persisted in
localStorage
The 5250 client lives under /tn5250/ and shares the toolbar, OIA,
canvas renderer, profile store, and EBCDIC tables with the 3270 side -
only the wire protocol and the datastream parser are separate.
It's tagged beta because it has had less mileage on real hardware than the 3270 side. Signon, WTD, input fields and AID keys work against pub400 and IBM i 7.x. The ENPTUI implementation is checked against the established 5250 clients, but uncommon host combinations still need end-to-end coverage. Please file issues with a screenshot and host.
Telnet / TN5250E (RFC 1205, RFC 4777):
- BINARY, EOR, SUPPRESS-GO-AHEAD, TERMINAL-TYPE, NEW-ENVIRON negotiation
- NEW-ENVIRON variables: optional
DEVNAME(blank lets the host allocate an available device),KBDTYPE,CODEPAGE,CHARSET, and the bypass-signon set (IBMRSEED,USER,IBMSUBSPWD) - 10-byte GDS record header on inbound and outbound records
- GDS opcodes: NO-OP, INVITE, OUTPUT-ONLY, PUT-GET, SAVE/RESTORE-SCREEN, READ-IMMEDIATE, MESSAGE-LIGHT on/off
- ATTN / SYSREQ flags, RFC 4777 Startup Response status, protocol negative responses, and complete System Request SAVE/RESTORE flow
5250 datastream:
- Commands:
WTD,WEC,WECW(write error code to window),CLEAR-UNIT,CLEAR-UNIT-ALT,CLEAR-FORMAT-TABLE,READ-INPUT-FIELDS,READ-MDT-FIELDS,READ-MDT-ALT,READ-IMMEDIATE,READ-MDT-IMMEDIATE-ALT,READ-SCREENbasic/extended and screen-to-print variants,WRITE-STRUCTURED-FIELD, full/partialSAVE-SCREENandRESTORE-SCREEN,ROLL - Orders:
SOH,RA,EA,ESC,TD,SBA,WEA,IC,MC,WTDSF,SF - Basic attribute table (0x20-0x3F) - color, reverse, underline, blink, column separator, non-display
- Field Format Word - bypass, dup, MDT, auto-enter, FER, monocase, mandatory, all SBCS data shifts, signed numeric, right-adjust / zero-fill; FCW transparency and metadata for resequencing, continued/word-wrapped segments, cursor progression, entry highlighting and pointer devices
- Separate datastream write address and visible cursor, standard/alternate geometry switching, virtual row-1/column-0 fields, and inclusive RA/EA
- AID keys: Enter, Clear, Help, PF1-24, Roll Up / Down / Left / Right, Print, Attn, SysReq
- Editing keys: Field Exit (Ctrl/Command+Enter or toolbar), New Line (Shift+Enter), Field +/- on the numeric keypad, Insert, Delete, Delete Word (Ctrl/Command+Backspace), Erase Field (Ctrl/Command+Shift+Backspace), Erase EOF (Alt+Delete), Erase Input (Ctrl/Command+Delete), Word Tab/Backtab (Ctrl/Command+Arrow), DUP, Field Mark, Home and End
- Bypass-signon: optional
USER/ password fields in the toolbar short-circuit the standard signon panel (RFC 4777 §5)
ENPTUI:
- WDSF (Write to Display Structured Field) decoder
- Primitives: Window, ScrollBar, Selection Field, menu/push-button variants, ruled grids, programmable mouse events, Write Data, and cascading removal
- IBM-compatible pseudo-field responses for selections, attached/standalone scrollbars, and two-event programmable mouse AIDs
- Keyboard/mouse interaction, selection character and mnemonic activation, restricted-window cursor, menu/pull-down cancellation, deselect-on-unlock, overlay rendering, and SAVE/RESTORE persistence
- Malformed structures are rejected with the matching
0x100501xxENPTUI sense code before the protocol-level negative response is sent - The 5292-2 Query Reply advertises enhanced FCWs/WDSFs and ENPTUI level 2 through the extended capability bytes; other models use the base reply
Models supported (5250):
| Model | Rows × Cols | Notes |
|---|---|---|
5251-11 |
24 × 80 | mono |
5291-1 |
24 × 80 | mono |
5292-2 |
24 × 80 | color, ENPTUI (default) |
3196-A1 |
24 × 80 | mono |
3179-2 |
24 × 80 | color |
3180-2 |
27 × 132 | mono |
3477-FC |
27 × 132 | color |
3477-FG |
27 × 132 | mono |
Four EBCDIC code pages ship today, switchable from the toolbar dropdown and persisted per connection profile:
- CP037 - US English EBCDIC. Default. Used by classic z/OS, Hercules turnkey, pub400 (IBM i), and most legacy mainframe shops.
- CP1047 - Latin-1 Open Systems EBCDIC. It differs from CP037 at the
bracket/caret-related punctuation positions (
[,],^,¬,¨, andÝ). Used by USS / z/OS Unix and modern hosts that interoperate with ASCII tooling. - CP500 - International Latin-1 EBCDIC, commonly used by multilingual host installations.
- CP1141 - German EBCDIC with the euro character.
Switching mid-session re-renders existing cells through the new table
immediately, no reconnect needed. Adding another code page is a 10-line
change - drop its delta map into DELTAS in src/proto/Ebcdic.js.
Targets evergreen browsers (Chrome / Edge / Firefox / Safari). Uses
ES modules, private class fields (#name), Uint8Array, Web
Audio (for the alarm beep) and localStorage (for profiles).
No transpilation, no bundler.
Canvas output is device-pixel-ratio aware, so terminal glyphs stay sharp on HiDPI/Retina displays.
The application remains build-free in the browser. Node.js 20 or newer is used for validation and by the dependency-free static server used on Railway:
npm start
npm run check
npm testOn Railway, npm start listens on the platform-provided PORT. The optional
container image runs the same Node.js static server and sends the same baseline
browser security headers:
docker build -t ironterm .
docker run --rm -p 8080:8080 irontermBypass-signon sends the RFC 4777 plaintext substitution-password form. The UI
requires wss:// and clears the password field on connect, but the relay is a
trusted endpoint and its TCP hop to port 23 may still be plaintext. Do not send
credentials through a relay you do not control or trust.
For wss:// with a self-signed certificate you have to visit
https://<bridge-host>:<port>/ once and accept the cert; the browser
won't surface a TLS prompt during a WebSocket handshake.
This is the realistic gap list. Nothing here blocks day-to-day TSO / CICS use on the 3270 side, or basic IBM i signon + green-screen use on the 5250 side.
- Additional SBCS pages can be added as verified mappings; DBCS and BIDI pages remain outside the current scope.
- DBCS / SO/SI (Asian double-byte)
- 5250 printer sessions - only display devices for now
- 5250 ENPTUI long tail - the actively implemented primitives are listed above; uncommon image/fax, video/audio, audit-window and vendor-specific WDSF structures are not advertised or decoded