Skip to content

Scripts & Native Binaries

The abracadabra module provides a generic solution for launching any local process as a door: native terminal applications, shell scripts, Python scripts, and more. Any process that communicates over stdio works. I/O is bridged through standard I/O (stdio) or a temporary TCP socket server.


The abracadabra config block supports the following fields:

ItemRequiredDescription
nameYesUsed as a key for tracking the number of clients using this door.
dropFileTypeNoType of drop file to generate. See Drop File Types. Can be omitted or none.
cmdYesPath to the executable to launch.
argsNoArray of arguments to pass to cmd. See Argument Variables below.
preCmdNoPath to a pre-command executable or script. Executes before cmd.
preCmdArgsNoArguments to pass to preCmd. See Argument Variables below.
cwdNoWorking directory for cmd. Defaults to the directory containing cmd.
envNoEnvironment variables as a map: { SOME_VAR: "value" }
nodeMaxNoMax concurrent sessions for this door. Uses name as the tracking key.
tooManyArtNoArt spec to display when nodeMax is exceeded.
minTimeLeftMinutesNoRefuse to start the door unless the user has at least this many minutes left in today’s time budget. Unset means no check; a user with no limit always passes.
notEnoughTimeArtNoArt spec to display when minTimeLeftMinutes is not met.
ioNoI/O mode: stdio (default) or socket. When socket, ENiGMA½ spawns a temporary TCP server on {srvPort} that the door process connects back to.
commTypeNoWhat the drop file tells the door it is talking to: local, serial, or socket, defaulting to socket when io: socket and local otherwise. dropFileType: BBSDEV takes a wider set with a different default — see BBSDEV.DRP.
commParamsNoThe descriptor, handle, UART base and IRQ, or FOSSIL port belonging to commType. Read only for dropFileType: BBSDEV. See BBSDEV.DRP below.
encodingNoThe door process’s text encoding. Defaults to cp437. Linux-native binaries often use utf8.

io says how ENiGMA½ talks to the process it spawns; commType says how the door talks to the caller. They are the same thing only when the process ENiGMA½ spawns is the door, which is why the default is derived from io:

commTypeReported asUse when
local (default for the legacy formats)DOOR32.SYS comm type 0, DOOR.SYS COM0:, DORINFO 0The door reads stdin and writes stdout. This covers io: stdio, which is nearly every native or scripted door.
serialDOOR32.SYS comm type 1, DOOR.SYS COM1:, DORINFO COM1An emulator sits between ENiGMA½ and the door and presents it a COM port — QEMU bridging {srvPort} onto isa-serial, for example.
socketDOOR32.SYS comm type 2, DOOR.SYS COM1:, DORINFO COM1Descriptor sharing by way of bivrost!.

Doors that ignore these fields entirely — most DOS-era games under an emulator — are unaffected by any of this.

dropFileType: BBSDEV writes a BBSDEV.DRP instead of one of the legacy formats.

The door is told where the file is through the BBSDEV_DRP environment variable, which ENiGMA½ sets to the full path before it spawns the process. The value is neither quoted nor shell-escaped, and a door reads it from its own environment. An env of your own still replaces ENiGMA½’s environment as it always has; BBSDEV_DRP is added to whichever environment the door gets.

commType names a wider set of mechanisms, and most of them take a parameter in commParams:

commTypecommParamsThe door is handed
localnoneits own local console
stdiononeterminal input on stdin, terminal output on stdout. This is the default under io: stdio
serialfile descriptoran inherited, configured POSIX serial descriptor
winserialWin32 HANDLEan inherited, configured Win32 COM handle
uartHHHH,I — I/O base in four uppercase hex digits, then the IRQdirect DOS UART access
fossilport, 0 through 254an initialized FOSSIL interface

The format also has a socket mode, for a socket the door inherits. ENiGMA½ never has one to pass on — io: socket stands up a listener the door dials — so commType: socket is refused here rather than written, whatever commParams you give it. A value for serial or winserial has to come from the emulator or bridge you put between ENiGMA½ and the door — QEMU, DOSEMU, bivrost!.

A channel ENiGMA½ cannot name stops the door from starting. Where the legacy formats fall back to local, local in this format is a positive claim — the door uses its current local console — so writing it for a door reading a socket would describe a screen nobody sees. Instead the drop file is refused, the door does not run, and the reason is logged. That is what happens under io: socket: the socket ENiGMA½ shares is a server the door dials rather than a descriptor it inherits, and the format has no token for that. A QEMU or DOSEMU setup says what the door really gets — commType: uart with commParams: 03F8,4, or fossil with 0 — and writes a valid file.

Line 12 names the character set of the door’s terminal data, and it is taken from the door’s own encoding rather than the caller’s terminal encoding — that is the value ENiGMA½ decodes the door’s output with, so the two cannot disagree. An encoding it cannot name in the registry’s spelling refuses the file rather than guessing; aliases iconv accepts, such as 437 or win1252, are folded onto the same name.

Line 13 must be a well-formed BCP 47 tag. A general.language that is not one — English (US), say — refuses the file rather than writing something a conforming door must reject.

Line 11, the forced logoff time, states when the caller’s daily time budget runs out, as an absolute UTC instant so a door that pauses cannot arrive at the wrong answer by counting down. It is written empty where nothing will end the session — no limit configured, or an exempt user — which is the format’s way of saying there is no deadline.

The following variables can be used in args and preCmdArgs:

VariableDescriptionExample
{node}Current node number1
{dropFile}Drop file filename onlyDOOR.SYS
{dropFilePath}Full path to the generated drop file/home/enigma/drop/node1/DOOR.SYS
{dropFileDir}Full path to the drop file directory/home/enigma/drop/node1/
{userAreaDir}User-specific save directory/home/enigma/drop/node1/NuSkooler/lord/
{userId}Current user ID42
{userName}Sanitized username (safe for filenames)nuskooler
{userNameRaw}Raw username (may not be filename-safe)\/\/izard
{srvPort}Temporary TCP server port (when io: socket)1234
{cwd}Working directory/home/enigma/doors/foo/
{termHeight}Terminal height25
{termWidth}Terminal width80
args: [
"-D", "{dropFilePath}",
"-N", "{node}",
"-U", "{userId}"
]

A simple wrapper script that launches a native binary:

doorMyGame: {
desc: My Door Game
module: abracadabra
config: {
name: MyGame
dropFileType: DOOR
cmd: /home/enigma/doors/mygame/launch.sh
args: [ "{node}", "{dropFilePath}" ]
nodeMax: 4
tooManyArt: DOORMANY
io: stdio
}
}
doorPythonGame: {
desc: Python Door
module: abracadabra
config: {
name: PythonGame
dropFileType: DORINFO
cmd: /usr/bin/python3
args: [ "/home/enigma/doors/pydoor/main.py", "{node}", "{dropFilePath}" ]
encoding: utf8
nodeMax: 8
io: stdio
}
}

Some doors require a socket connection rather than stdio. ENiGMA½ starts a temporary TCP server and passes the port to your script:

doorSocketGame: {
desc: Socket Door
module: abracadabra
config: {
name: SocketGame
dropFileType: DOOR
cmd: /home/enigma/doors/socketgame/launch.sh
args: [ "{node}", "{dropFile}", "{srvPort}" ]
nodeMax: 1
io: socket
}
}

Due to Node.js limitations, ENiGMA½ does not directly support DOOR32.SYS-style socket descriptor sharing. However, bivrost! bridges this gap. bivrost! is available for Windows and Linux x86/x86_64 (and buildable from Rust on other platforms).

doorWithBivrost: {
desc: Bivrost Example
module: abracadabra
config: {
name: BivrostExample
dropFileType: DOOR32
cmd: /home/enigma/utils/bivrost
args: [
"--port", "{srvPort}",
"--dropfile", "{dropFilePath}",
"--out", "/home/enigma/doors/jezebel",
"/home/enigma/doors/jezebel/door.exe /home/enigma/doors/jezebel/door32.sys"
]
nodeMax: 1
tooManyArt: DOORMANY
io: socket
}
}

See the bivrost! documentation for details. Pre-built binaries are also available via Phenom Productions on various boards.

Alternative workarounds: Telnet Bridge, or NET2BBS.