Native Doors

June 23, 2026 · View on GitHub

See also: Doors.md for an overview of all door types and shared multiplexing bridge setup.

Table of Contents


Native doors are BBS door programs that run as native Linux binaries or Windows executables, launched directly via PTY (pseudo-terminal). Unlike DOS doors, they require no emulator — the program runs as a regular system process with full ANSI/VT100 terminal support.

Multiplexing Bridge Setup

Native doors use the same multiplexing bridge as DOS doors. Before native doors will work, the bridge must be installed and running.

Quick start:

# Install bridge dependencies (includes node-pty for native door PTY support)
cd scripts/dosbox-bridge
npm install

# Start the bridge (interactive)
node multiplexing-server.js

# Or run as a background daemon
node multiplexing-server.js --daemon

For full setup instructions including production service configuration, environment variables, and reverse proxy setup, see Doors.md.


How It Works

  1. A user clicks Launch on a native door from the /games page.
  2. The web interface creates a door session via the API and opens the xterm.js terminal player in an iframe.
  3. The multiplexing bridge (Node.js) reads the session from the database, spawns the door executable via node-pty, and bridges the WebSocket to the PTY.
  4. A DOOR.SYS drop file is written to native-doors/drops/NODE{n}/DOOR.SYS and user data is injected as environment variables.
  5. When the door exits (or the user disconnects), the PTY is killed and the session is cleaned up.

File Structure

binkterm-php/
├── native-doors/
│   ├── doors/                              # Install doors here
│   │   ├── linuxdoortest/                  # Bundled test door (Linux)
│   │   ├── windoortest/                    # Bundled test door (Windows)
│   │   └── mydoor/                         # Example custom door
│   │       ├── nativedoor.json             # Door manifest (required)
│   │       ├── mydoor.sh                   # Executable (or binary, .bat, etc.)
│   │       └── icon.png                    # Optional icon (64×64 PNG)
│   └── drops/                              # Generated at runtime — do not edit
│       ├── NODE1/
│       │   └── DOOR.SYS
│       └── NODE2/
│           └── DOOR.SYS
└── config/
    └── nativedoors.json                    # Runtime config (managed by admin panel)

Each door lives in its own subdirectory under native-doors/doors/. The directory name is the door's ID — it is used in URLs and the database, so it must be lowercase with no spaces (e.g. lord, mygame, linuxdoortest).

Creating a New Door

1. Create the door directory

mkdir -p native-doors/doors/mydoor

2. Create the manifest

The easiest way is through the admin interface:

  1. Go to Admin → Native Doors.
  2. In the Add New Door panel, find your door directory and click Create Manifest.
  3. Fill in the form. Set Executable to the script or binary that launches the door (e.g. mydoor.sh).
  4. Click Create Manifest to save.

The manifest is saved as native-doors/doors/mydoor/nativedoor.json. See the manifest format section for the full field reference. A minimal example:

{
  "type": "nativedoor",
  "version": "1.0",
  "managed": "web",
  "game": {
    "name": "My Door",
    "short_name": "MYDOOR",
    "author": "Author Name",
    "version": "1.0",
    "release_year": 2026,
    "description": "A short description shown in the game library.",
    "genre": ["Action"],
    "players": "Single-player",
    "icon": null,
    "screenshot": null
  },
  "door": {
    "executable": "mydoor.sh",
    "launch_command": "/bin/bash mydoor.sh",
    "dropfile_format": "DOOR.SYS",
    "max_nodes": 10,
    "ansi_required": false,
    "time_per_day": 30
  },
  "requirements": {
    "admin_only": false
  },
  "config": {
    "enabled": false,
    "credit_cost": 0,
    "max_time_minutes": 30,
    "max_sessions": 10
  }
}

3. Place the executable

Copy your program into the door directory. Make sure it is executable on Linux:

chmod +x native-doors/doors/mydoor/mydoor.sh

For compiled binaries, the same applies:

chmod +x native-doors/doors/mydoor/mydoor

4. Enable the door in the admin panel

  1. Go to Admin → Native Doors.
  2. Click Sync Doors to import newly installed doors.
  3. Find your door in the list and toggle it on.
  4. Click Save Configuration.

The door will now appear in the /games game library.


nativedoor.json Format

Top-level fields

FieldTypeRequiredDescription
typestringYesMust be "nativedoor"
versionstringYesManifest format version. Use "1.0"
gameobjectYesGame metadata (see below)
doorobjectYesLaunch and technical settings (see below)
requirementsobjectNoAccess requirements (see below)
configobjectNoDefault runtime configuration (see below)

game object

FieldTypeRequiredDescription
namestringYesDisplay name shown in the game library
short_namestringNoAbbreviated name (uppercase, no spaces). Defaults to name
authorstringNoAuthor or publisher. Defaults to "Unknown"
versionstringNoDoor game version number
release_yearintegerNoYear the door was written or released
descriptionstringNoShort description shown on the game card
genrearrayNoArray of genre strings, e.g. ["RPG", "Strategy"]
playersstringNoPlayer count description. Defaults to "Single-player"
`icon$\text{string}|\text{null}\text{No}\text{Filename} \text{of} \text{an} \text{icon} \text{image} (64 \times 64 \text{PNG}) \text{in} \text{the} \text{door} \text{directory}
$screenshot`string|nullNoFilename of a screenshot image in the door directory

door object

FieldTypeRequiredDescription
executablestringYesFilename of the main executable relative to the door directory
launch_commandstringNoFull command to run. Supports {node}, {dropfile}, and {user_number} placeholders (see below). Defaults to executable
dropfile_formatstringNoDrop file format. "DOOR.SYS" (default) or "DOOR32.SYS"
output_encodingstringNoCharacter encoding of the door's output. "utf8" (default) or "cp437". Use "cp437" for legacy DOS-style doors that output CP437 box-drawing and ANSI art
max_nodesintegerNoMaximum simultaneous sessions. Defaults to 10
ansi_requiredbooleanNoWhether ANSI is required. Defaults to true
time_per_dayintegerNoTime limit in minutes per day. Defaults to 30

Launch command placeholders

The launch_command string may contain the following placeholders, which are substituted at launch time:

PlaceholderReplaced with
{node}Node number (e.g. 1)
{dropfile}Full path to the DOOR.SYS file (e.g. /srv/bbs/native-doors/drops/NODE1/DOOR.SYS)
{user_number}BBS user ID (numeric)

Examples:

"launch_command": "/bin/bash mydoor.sh"
"launch_command": "./mydoor --node {node} --dropfile {dropfile}"
"launch_command": "cmd.exe /c mydoor.bat"

If launch_command is omitted, executable is used directly as the command with no arguments.

requirements object

FieldTypeDefaultDescription
admin_onlybooleanfalseIf true, only admin users can launch the door

config object

These are the default settings applied when a door is first synced. They can be overridden at any time through Admin → Native Doors without editing the manifest.

FieldTypeDefaultDescription
enabledbooleanfalseWhether the door is available to users. Always false in the manifest — enable through the admin panel
credit_costinteger0Credits deducted per session launch (0 = free)
max_time_minutesinteger30Maximum session length in minutes
max_sessionsinteger10Maximum concurrent sessions

Environment Variables

The following environment variables are set in the door process at launch:

VariableDescriptionExample
DOOR_USER_NAMEUser's handleSysop
DOOR_USER_REAL_NAMEUser's real nameJohn Smith
DOOR_USER_NUMBERBBS user ID (numeric)42
DOOR_NODENode number1
DOOR_BBS_NAMEBBS name from configurationMy BBS
DOOR_DROPFILEFull path to the DOOR.SYS file/srv/bbs/native-doors/drops/NODE1/DOOR.SYS
DOOR_ANSIAlways 1 (ANSI assumed)1
TERMTerminal typexterm-256color

The process also inherits the environment of the multiplexing bridge, including PATH.


Drop File

A drop file is generated and written to native-doors/drops/NODE{n}/ before the door is launched. Two formats are supported, selected via dropfile_format in the manifest:

DOOR.SYS (default)

The classic 52-line format compatible with most traditional BBS door games. Written to DOOR.SYS.

"dropfile_format": "DOOR.SYS"

DOOR32.SYS

An 11-line format designed for modern doors running over telnet/socket connections. Written to DOOR32.SYS. Use this for doors that expect a socket-style connection rather than a serial/FOSSIL interface.

"dropfile_format": "DOOR32.SYS"

The DOOR32.SYS fields are:

LineFieldValue
1Comm type2 (telnet/socket)
2Comm handle0
3Baud rate0
4BBS nameFrom user data
5User record numberFrom user data
6User's real nameFrom user data
7User's handle/aliasFrom user data
8Security levelFrom user data
9Time left (minutes)From user data
10ANSI1 (always)
11Node numberSession node number

The same user data is also available via environment variables (see above), so simple doors do not need to parse either drop file format at all.


Terminal Settings

Doors run in an xterm-256color PTY. ANSI escape sequences are passed through directly — no character encoding conversion is applied (unlike DOS doors which convert CP437). Write UTF-8 or plain ANSI to stdout.

Terminal size

The default PTY size is 80 columns × 25 rows. For doors that benefit from a wider canvas (such as PubTerm), the initial terminal dimensions can be set per-door in config/nativedoors.json via the terminal_size key:

{
  "pubterm": {
    "enabled": true,
    "terminal_size": "132x43"
  }
}

Accepted values are "WxH" strings (e.g. "80x25", "132x24", "132x43", "132x50") or "autofit" to size the canvas to the user's browser window. The PTY is spawned at the configured dimensions and the BBS receives the correct NAWS terminal size at connection time.

Note: "autofit" sizes the xterm.js canvas to fill the browser window and sets the initial BBS dimensions accordingly. Mid-session resize (NAWS updates when the user resizes their browser) is not currently supported — the system telnet client used by PubTerm does not forward PTY window-change signals to the BBS. The BBS will start at the correct size but will not adapt if the user resizes their browser mid-session.

The terminal_size setting is read from config/nativedoors.json (the admin-configurable runtime config) rather than the static nativedoor.json manifest, so it can be changed through Admin → Native Doors without touching the door files.


Platform Notes

Linux

  • Shell scripts must have a shebang line (e.g. #!/bin/bash)
  • Compiled binaries must be built for the host architecture
  • Executables must have the execute bit set (chmod +x)

Windows

  • Use cmd.exe /c in launch_command for .bat files:
    "launch_command": "cmd.exe /c mydoor.bat"
    
  • Use the full path to an interpreter if it is not on PATH

Configuration File

Runtime configuration (enabled/disabled, credit cost, session limits) is stored in config/nativedoors.json. This file is managed by the admin panel and the admin daemon — do not edit it directly while the BBS is running.

The file structure is a JSON object keyed by door ID:

{
  "linuxdoortest": {
    "enabled": true,
    "credit_cost": 0,
    "max_time_minutes": 30,
    "max_sessions": 10
  },
  "mydoor": {
    "enabled": false,
    "credit_cost": 5,
    "max_time_minutes": 60,
    "max_sessions": 5
  }
}

All supported keys per door entry:

KeyTypeDefaultDescription
enabledbooleanfalseWhether the door is available to users
credit_costinteger0Credits deducted per session
max_time_minutesinteger30Maximum session length in minutes
max_sessionsinteger10Maximum concurrent sessions
allow_anonymousbooleanfalseAllow unauthenticated guest access (requires credit_cost: 0)
guest_max_sessionsinteger2Maximum concurrent guest sessions when allow_anonymous is true
terminal_sizestring"80x25"Initial PTY and canvas dimensions. Accepted values: "80x25", "132x24", "132x43", "132x50", or "autofit". See Terminal Settings

Included Test Doors

Two test doors are bundled in native-doors/doors/ to verify the system is working:

Door IDPlatformDescription
linuxdoortestLinuxBash script — displays "hello world" and waits for a keypress
windoortestWindowsBatch file — displays "hello world" and waits for a keypress

Both are disabled by default. Enable them through Admin → Native Doors to test your setup.


Security Warning

Only install native doors from sources you trust.

Native doors run as the same operating system user as the BinktermPHP web server and multiplexing bridge. A door that drops to a shell, spawns subprocesses, or reads arbitrary files on disk does so with the full permissions of that user — including access to your database credentials, configuration files, private keys, and all BBS data.

Key risks to be aware of:

  • Shell escape — if a door provides any mechanism to execute shell commands (e.g. a built-in editor, help viewer using less, or debug mode), a user can break out and run arbitrary commands on the server.
  • File system access — the door can read and write any file the web server user can access, including .env, config/binkp.json, and the database.
  • Network access — the door can make outbound network connections.

Troubleshooting

Door does not appear in the game library after sync

  • Confirm nativedoor.json exists in the door directory and is valid JSON
  • Check that "type": "nativedoor" and "game.name" are present
  • Check the PHP error log for manifest parse errors

Door launches but the screen is blank

  • Confirm the executable exists and is executable (chmod +x)
  • Confirm the launch_command path is correct
  • Test the command manually in a terminal to verify it runs

Door exits immediately

  • Run the executable manually in a terminal to see error output
  • Check that all required dependencies (libraries, interpreters) are installed on the host

Drop file is not being written

  • Confirm native-doors/drops/ exists and is writable by the user running the bridge
  • The bridge creates NODE{n} subdirectories automatically

Session does not clean up after exit

  • Ensure the door process exits cleanly when stdin is closed or a hangup signal (SIGHUP) is received
  • The bridge sends SIGHUP to the PTY when the user disconnects