A MUD code base in the style of the last millennium, written in the new millennium.
- Introduction
- Features
- Build Requirements
- Building
- Releases
- Deploying
- Local Configuration (.env)
- Database Tools
- Running the Server
- Development
- Support
- Contributing
- License
Cyberspace. A consensual hallucination experienced daily by billions of legitimate operators, in every nation, by children being taught mathematical concepts... A graphic representation of data abstracted from the banks of every computer in the human system. Unthinkable complexity. Lines of light ranged in the nonspace of the mind, clusters and constellations of data. Like city lights, receding. -- William Gibson
Boris MUD is a text-based virtual reality that allows multiple people to engage in roleplaying, adventuring, and story-telling.
- MTH (Mud Telopt Handler) - standardized handling of TELNET protocol.
- LMDB back-end database for objects and user accounts.
- Room navigation: look, go, enter, and direction aliases (north/n, south/s, etc.).
- SSE-based web client with output buffering and multipart framing.
- ColdFire machine programs -- objects can have ColdFire V4e programs (ELF binaries) attached that post messages to the game world via hypercalls.
- TODO ability to host multiple independent worlds from a single server.
- TODO On-Line Creation: interactive wizard provides menu-based building.
- GNU Make 4.2.1 (or later)
- GCC or Clang
- zlib development headers
Optional (for rebuilding machine programs in sdk/machine/):
- m68k-linux-gnu-gcc cross-compiler (
apt install gcc-m68k-linux-gnuon Debian/Ubuntu)
Debian / Ubuntu:
sudo apt update
sudo apt install build-essential zlib1g-devgit clone --recurse-submodules https://github.com/OrangeTide/boris
cd borisBuild using all available CPU cores:
make -j$(nproc)To use Clang instead of GCC:
make -j$(nproc) USE_CLANG=1To specify a cross-compiler directly:
make -j$(nproc) CC=arm-linux-gnueabihf-gccBuild output binaries are boris, mkpass, and muddb-tool under
_out/<triplet>/bin/ (e.g. _out/x86_64-linux-gnu/bin/). Object files go to
_build/<triplet>/, so cross-compiled objects and binaries don't clobber
native ones. make install stages the binaries and web client into bin/
for packaging and deployment.
To install the web client (to bin/www/ by default):
make installEnable the arm64 architecture and install the cross toolchain with libraries:
sudo dpkg --add-architecture arm64
sudo apt update
sudo apt install gcc-aarch64-linux-gnu zlib1g-dev:arm64Note: On Ubuntu and derivatives, the default apt mirrors only carry amd64/i386
packages. arm64 and armhf packages are served from ports.ubuntu.com. If apt update
fails with 404 errors for arm64, you need to add a ports source and pin
your existing sources to amd64 only. Create
/etc/apt/sources.list.d/arm64-cross.list:
deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports noble main universe
deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports noble-updates main universe
Then add [arch=amd64,i386] to each deb line in your existing
/etc/apt/sources.list (or files under sources.list.d/) so they stop
trying to fetch arm64 from the wrong mirror. Then run sudo apt update again.
Build with the cross-compiler:
make -j$(nproc) CC=aarch64-linux-gnu-gccFor a fully static binary (no runtime dependencies on the Pi):
make -j$(nproc) CC=aarch64-linux-gnu-gcc LDFLAGS="-static"Follow the same ports.ubuntu.com setup as above (substituting [arch=armhf]
for [arch=arm64], or [arch=arm64,armhf] if you want both) if needed, then:
sudo dpkg --add-architecture armhf
sudo apt update
sudo apt install gcc-arm-linux-gnueabihf zlib1g-dev:armhfmake -j$(nproc) CC=arm-linux-gnueabihf-gccCopy bin/boris, bin/mkpass, boris.cfg, and the data/ directory to the Raspberry Pi to run.
make clean # Remove object files and dependency files
make distclean # Remove objects, binaries, and installed web assetsBuild a release tarball containing stripped binaries, web client, sample
config, and game data. Pass all build flags in the same command -- release
chains through the full build so CC, LDFLAGS, and RELEASE_ARCH must
all be present:
make -j$(nproc) release # native x86-64
make -j$(nproc) release CC=aarch64-linux-gnu-gcc LDFLAGS="-static" RELEASE_ARCH=linux-arm64
make -j$(nproc) release CC=arm-linux-gnueabihf-gcc LDFLAGS="-static" RELEASE_ARCH=linux-arm32Output is a file like boris-0.7-linux-arm64.tar.xz in the project root.
Deploy a release to a remote server via SSH. Include the same build flags
as you would for make release:
make -j$(nproc) deploy DEPLOY_DEST=jon@myserver.com:mud
make -j$(nproc) deploy CC=arm-linux-gnueabihf-gcc LDFLAGS="-static" RELEASE_ARCH=linux-arm32 DEPLOY_DEST=jon@myserver.com:mudThis builds a release tarball and uploads it to the remote host. The deploy script uses versioned releases with symlinks:
~/mud/
releases/boris-0.7-linux-arm64/ # each release unpacked here
shared/boris.cfg # persistent config (survives upgrades)
shared/data/chars/ # persistent game data
shared/data/users/
current -> releases/... # symlink to active release
On first deploy, boris.cfg is copied to shared/ for editing. Game data
directories (chars/, users/) are symlinked from each release into
shared/ so they persist across upgrades.
You can also deploy directly without the Makefile:
./scripts/deploy.sh jon@myserver.com:mud [tarball]Per-checkout settings go in a .env file (not committed to git). The
Makefile reads it automatically. Copy the example to get started:
cp doc/env.example .envAvailable settings:
| Variable | Description | Example |
|---|---|---|
DEPLOY_DEST |
Deploy target for make deploy (user@host[:dir]) |
jon@myserver.com:mud |
RELEASE_ARCH |
Override release architecture label | linux-arm32 |
CC |
C compiler (useful for cross-compilation) | arm-linux-gnueabihf-gcc |
LDFLAGS |
Linker flags | -static |
Boris stores game data (rooms, characters, users) in an LMDB database
(data/muddb/) by default. The muddb-tool utility exports and imports
this data as plain-text JSON files organized by domain. World objects can
optionally be moved to a content-addressed store instead, see
Object Store Backends.
Dump the entire database to a directory tree:
./bin/muddb-tool export data/muddb dump/This creates dump/<domain>/<key>.json for every object. To export a
single domain:
./bin/muddb-tool export data/muddb dump/ roomsLoad JSON files into the database:
./bin/muddb-tool import data/muddb dump/To import only specific domains:
./bin/muddb-tool import data/muddb dump/ rooms charsA sample/ directory ships with the source (and binary releases) containing
a starter world with a handful of connected rooms. To set up a fresh
database:
mkdir -p data/muddb
./bin/muddb-tool import data/muddb sample/Then start the server normally with ./bin/boris.
The export/import format is a flat directory hierarchy:
sample/
rooms/
town-square.json # {"id":"town-square","name.short":"Town Square", ...}
east-gates.json
...
Each .json file contains a single JSON object. The filename (minus .json)
is the database key.
World objects (the objs domain: rooms, exits, machine programs) can be
served by one of two backends, selected with database.backend in
boris.cfg:
- muddb (default): the LMDB database at
data/muddb/, described above. - cas: a content-addressed depot (smolvfs). Every commit is an immutable snapshot, so the depot keeps a history of world states.
User accounts, characters, and invites always live in LMDB regardless of
this setting, so data/muddb/ is required either way.
Configuration keys:
| Setting | Default | Description |
|---|---|---|
database.backend |
muddb |
Object store backend: muddb or cas |
database.cas.path |
data/casdb |
Depot directory for the cas backend |
database.cas.ref |
world |
Named snapshot reference inside the depot |
database.cas.retain |
0 | Keep the last N snapshots; 0 keeps all history |
database.cas.commit_seconds |
60 | Interval between automatic snapshot commits |
To switch an existing MUD to the cas backend, stop the server, import the
objects into a depot, and enable the backend in boris.cfg:
./bin/muddb-tool to-cas data/muddb data/casdbdatabase.backend = cas
database.cas.path = data/casdb
database.cas.retain = 8
Notes for operating the cas backend:
- Object edits are saved to the depot as they happen, but only become part
of a snapshot at the next commit. Commits run every
database.cas.commit_secondsand once more at clean shutdown. After a crash, the world reverts to the last committed snapshot, losing at most one commit interval of edits. - With
database.cas.retain = 0the depot grows without bound, because every snapshot is kept forever. Set it to a small number (for example 8) to prune old snapshots automatically; the depot then converges to a bounded size. - There is currently no cas-to-muddb export. Keep your LMDB data (or a JSON export of it) until you are confident in the cutover, and back up the depot as described below.
The server runs unattended on hobby hardware, so regular backups are important. Two approaches:
JSON export (portable, human-readable)
./bin/muddb-tool export data/muddb backup/This creates one .json file per record. The export can be inspected,
edited, diffed, or version-controlled. To restore from a JSON export, stop
the server, then:
rm -rf data/muddb
mkdir -p data/muddb
./bin/muddb-tool import data/muddb backup/LMDB file copy with mdb_copy (fast, compact)
mdb_copy creates a consistent snapshot even while the server is running.
Install it from the system LMDB package (apt install lmdb-utils on
Debian/Ubuntu).
mdb_copy data/muddb /path/to/backup/muddbAdd -c to compact free pages during copy:
mdb_copy -c data/muddb /path/to/backup/muddbTo restore, stop the server and replace the database directory:
rm -rf data/muddb
cp -r /path/to/backup/muddb data/muddbCAS depot copy (cas backend only)
Both methods above cover only the LMDB database. When
database.backend = cas, world objects live in the depot directory and
must be backed up separately with a plain file copy:
cp -a data/casdb /path/to/backup/casdbDepot files are immutable once written and the snapshot reference is
updated by atomic rename, so copying while the server runs is safe. The
only files that can disappear during a live copy are old snapshots pruned
by database.cas.retain. To restore, stop the server and copy the depot
back into place.
Automated daily backup (cron)
Back up with muddb-tool, keep 14 days of snapshots. Adjust MUD_DIR to
match your installation:
MUD_DIR=/home/mud/boris
# daily database export, 04:00, keep 14 days
0 4 * * * d=$MUD_DIR/backups/$(date +\%F); mkdir -p "$d" && $MUD_DIR/bin/muddb-tool export $MUD_DIR/data/muddb "$d" && find $MUD_DIR/backups -maxdepth 1 -mindepth 1 -mtime +14 -exec rm -rf {} +
Or with mdb_copy for a faster binary snapshot:
MUD_DIR=/home/mud/boris
# daily LMDB snapshot, 04:00, keep 14 days
0 4 * * * d=$MUD_DIR/backups/muddb-$(date +\%F); mkdir -p "$d" && mdb_copy -c $MUD_DIR/data/muddb "$d" && find $MUD_DIR/backups -maxdepth 1 -name 'muddb-*' -mtime +14 -exec rm -rf {} +
Edit configuration file (boris.cfg) with your preferred MUD port (server.port, default 4444) and web port (webserver.port, default 8080).
Initialize the database with the sample world (optional but recommended):
mkdir -p data/muddb
./bin/muddb-tool import data/muddb sample/Start the server:
./bin/borisLogin and create your account.
When running the server the web client will be hosted at http://localhost:8080 (or your configured webserver.port).
Press Ctrl+C to shut down the server gracefully.
TODO: provide instructions on how to manually set administrator privileges on an account
CRITICAL:telnetserver:telnet server error: could not bind socket (Address already in use)
Another process is already listening on the configured port. Find it with:
lsof -i tcp:4444Either stop the other process or change server.port in boris.cfg to an unused port.
ERROR:muddb:mdb_env_open(data/muddb): No such file or directory
The LMDB database directory does not exist. Create it:
mkdir -p data/muddbClients cannot connect (firewall blocking ports)
If ufw is active on the server, open the telnet and web ports:
sudo ufw allow 4444/tcp
sudo ufw allow 8080/tcpReplace the port numbers if you changed server.port or webserver.port in
boris.cfg. Check status with sudo ufw status.
Objects can have ColdFire V4e programs attached to them. Pre-built ELF
binaries ship in data/machine/ and are loaded automatically when the
object enters the cache.
Source code for machine programs lives in sdk/machine/. To rebuild:
cd sdk/machine
makeThis cross-compiles with m68k-linux-gnu-gcc and writes ELF files to
data/machine/. The SDK header sdk/machine/include/machine_hc.h provides
inline wrappers for all available hypercalls (sleep, message post, exit, etc.).
See DEV.md for the machine program architecture details.
See DEV.md for architecture, build system internals, code patterns, and contributor information.
Please open an issue for support.
Please contribute using Github Flow. Create a branch, add commits, and open a pull request.
Boris includes the following third-party libraries:
- tiny-AES-c -- Small portable AES128/192/256 in C. Public domain (Unlicense). Used for scrypt password encryption.
- LMDB -- Lightning Memory-Mapped Database.
- jsmn -- Minimalistic JSON parser.
- MTH -- MUD Telopt Handler.
Copyright (c) 2008-2025, Jon Mayo
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
The views and conclusions contained in the software and documentation are those
of the authors and should not be interpreted as representing official policies,
either expressed or implied, of the Boris MUD project.