The dim, block-charactered Terminal interface is the go-to for popular media about hackers, code crackers, and sci-fi fiction. They provide a means of interacting with a computer or computer system, but do so in an exact, though admittedly sometimes cumbersome, manner.

For those familiar with them, the terminal is a formidable tool, granting full use of the data, networking, and computation of a powerful computer. For those not familiar, it can be a bit harrowing, or can be simplified to act more like an olde-tyme text adventure. For dungeon masters, terminals provide an opportunity for the players to interact with the world, and the wise DM can tailor their terminals to their campaign's scope and their party's ability.

Why Use a Terminal?

Terminals offer a special sort of interaction that is altogether different from your standard role-playing experience. A huge amount of hacker culture hype surrounds these textual interfaces, allowing players to feel leet, but at the same time feel like they are solving a puzzle or unraveling a mystery. Human interaction is often off-the-cuff. Terminal interfaces were crafted ahead of time, and could contain all sorts of clues, useful information, and awesome capabilities if only the user can figure it out.

Terminals would most typically show up in tech-savy campaigns, and I have seen them used on space ships, on facility computers, interfaces for advanced vehicles and weaponry, and I have even used something similar in a Tom-Riddle-esque tome.

Players can use terminals to gain information, execute programs, send messags, lock/unlock doors, trigger machinery, play movie/sound/hologram records, tap into camera feeds, steal government secrets, chat with other people or bots, commit sabotauge, accidentally release Skynet, etc.

Types of Role-Playing Terminals

Bash: The GNU Bourne-Again Shell

Want the full shell experience? Go with the real thing.

The Bourne-Again Shell, or Bash, is the default terminal on the Ubuntu Linux OS. This is a real terminal that real hackers use every day. Bash gives you full access to everything, and thus it is very easy to get up and running with all that you need. Players can find files, open and edit files, navigate through directories, execute programs, etc.

The problem, of course, is that users can do *anything*, and you probably don't want them erasing your hard drive or navigating off to things unrelated to the role-playing experience.

Completely containing a player character is difficult, but in most cases you should be able to trust your user, especially if you are standing right there making sure they aren't up to something malicious or severely misguided. Nevertheless, some options are:

  • creating a guest account with limited privilege
  • disabling dangerous commands such as `rm`
  • giving the players a list of acceptable commands, like `ls`, `cd`, etc.

Using a real terminal gives you a lot of freedom.

  • build filesystem hierarchies with data from your nefarious cult
  • require users to figure out passwords
  • encrypted files or files with encrypted content
  • let players show off their grep skills
  • let players show off their bash scripting / perl skills
  • write custom programs that can be executed from the terminal
  • easily incorporate non-textual content such as sound files, movies, and images

Orated interaction

Want the easiest interaction? Orated interaction is your good-old role-playing setup. The DM describes what happens and the players describe what they do.

Simple. Easy.

The problem with this approach is that DMs are not computers, and cannot keep up with players in the same way a terminal can. Inconsistencies can easily arise, DMs can forget things.

Text Adventure

Programs exist to create your own text adventures. You can leverage these tools to create terminals. Examples include Quest, TADS, and ADRIFT. They are all just a Google search away!

Different tools come with different learning curves and capabilities.

Preprinted transcripts

These are computer terminal outputs that the DM prepares ahead of time. They look like the text at the very beginning of the article - basically a back and forth between some previous user and the machine.

While the preprinted transcript lacks depth interaction, it is relatively easy to prepare, allows for abundant customization, lets the DM control exactly what is shown ahead of time, and makes for neat in-game props.

Preprinted transcripts can also be combined with other forms of terminals.

  • a printout of the command history (such as what happens when `history` is run in Bash), and the players can deduce things from it
  • a user accidentally typed their password in place of their username
  • IM chat between characters
  • evidence that certain files were copied to a USB, deleted, or both
  • that a certain someone was in at a certain time, perhaps they ran some unsavory commands (like disabling security systems)

Custom shell

When all else fails you can program your own shell.

This is almost certainly the most time-intensive option but it affords the most control. Granted, this will either be out of reach due to lack of expertise or time for most DMs.

Some things you can do with custom shells:

  • restrict users to only have specific commands
  • minimize the chance that users break something
  • include a state that can be saved periodically and reverted to in the event of a crash or when you have to stop for the night
  • easily boot up with a history log, etc. nicely planted in there for players to find
  • easily customize the text for every command
  • easily launch programs in response to commands; such as if you want to implement something like the interface in War Games
  • add in interactive features that you control from another computer, such as chatting with a malicious AI, the activation of events, etc.

Custom Julia Shell Tips

I wrote my own custom shell for a role-playing one-shot. It was a pain in the butt, but potentially worth it. This section details my basic approach and presents some tips and tricks for doing the same thing yourself.

I wrote it in Julia. You can install julia here or try it online using JuliaBox.

Basic REPL

At its core, the terminal is a window where the user can type things in and get responses out.

>> hello
`HELLO` command not recognized. Use `?` to list commands
>> ?
STATUS Display system status
LOAD Load program from spool
LIST Display an ordered collection
PLAY Execute installed game
1 - USER
2 - CPU

It turns out you can get pretty far by running a Julia program in a Unix terminal and running a read-eval-print (REPL) loop:

while true
 print('>>  ')
 cmd = uppercase(strip(readline(STDIN)))
 if ismatch(r'\?', cmd)
        # display allowed commands
        for (name, desc) in 
                ('STATUS', 'Display system status'),
                ('LOAD', 'Load program from spool'),
                ('LIST', 'Display an ordered collection'),
                ('PLAY', 'Execute installed game'),
            @printf('   %-10s  %s\n', name, desc)
 elseif ismatch(r'STATUS', cmd)
    # do stuff
 elseif ismatch(r'LOAD', cmd)
    # do other stuff
    @printf('`%-4s` command not recognized. Use `?` to list commands', cmd)

All this does is:

  1. wait for the next line of player input
  2. check what to do based on that
  3. repeat

Save this code to a file, then run it and bam! Instant basic custom terminal.

Basic History Log

One nice feature to have for both authenticity purposes but also for hiding flavorful information is to include a command log. Every time users enter text, this text can be logged to that history. In bash, the history can be viewed with the history command to see what you did in the past.

What did the last survivors of the starship frigate do as they were slowly running out of oxygen? Did Nefarious Agent Bob sneak a few commands in while you went out to grab your coffee? What programs and files are used most often? In game this is great to players for the usual reasons, but as a DM you can also initialize the history with commands used by some NPC in the past.

type HistoryEntry
 command::ASCIIString   # the command run
 date::DateTime         # the date of execution
hist = HistoryEntry    # history log
while true
    print('>> ')
    cmd = uppercase(strip(readline(STDIN)))
    push!(hist, HistoryEntry(cmd, now()))    # append command to the history    ... # do your if statements
    elseif ismatch(r'HIST', cmd)
        for histentry in reverse(hist)
            d =
            @printf('  d.d.d d:d  %-4s\n', 
      , Dates.month(d), mod(Dates.year(d), 100), 
                Dates.hour(d), Dates.minute(d), histentry.command)


05.08.16 21:11 HIST
11.04.91 09:21 DELETE evidence.txt
11.04.91 09:18 UNMOUNT flashdrive
11.04.91 09:15 COPY evidence.txt flashdrive/evidence_copy.txt


Basic God Mode

Here is a feature I am really excited about. Best part is, it is really easy to implement.

As a DM you want to be able to add a whole lot of content without a lot of work. Coding things by hand takes a lot of time. But you have a whole plethora of other material available to you - other programs, images, video, sound files, etc. Furthermore, you might want to have text appear on your player's screen, or have the text be typed by you on the fly. This is especially important if you want to have them `chat` with NPCs - such as with a ghost in the shell or a hacker secret agent.

The code below sets up a simple TCP socket that listens for commands from another Julia process. You can then send whatever you want from another process by connecting to that TCP socket. This can work across computers! Run things from a laptop as you watch the PCs mess around with the host computer.

Player Computer

const MSG_TYPE_PRINT = 1
@async begin
    server = listen(2001)
    while true
        sock = accept(server)
        @async while isopen(sock)
            line = strip(readline(sock))
            # first value will be the command type
            if ismatch(r'_\d+_', line)
                msg_type = parse(Int, match(r'(?<=_)\d+', line).match)
                payload = line3+length(msg_type) : end
                if msg_type == MSG_TYPE_PRINT
                elseif msg_type == MSG_TYPE_EXCEUTE
                    cmd = ``
                    append!(cmd.exec, split(payload, ' '))

God Computer

io = connect(target_computer, 2001) # replace target_computer
println(io, '_1_Hello Players')

The basic code above just infinitely waits for messages to come in.

  • _1_stuff will print stuff to the terminal. Use this for chats
  • _2_stuff will execute stuff in the unix shell. Use this to:
    • display images via `eog` or similar. Ex: _2_eog villain_face.png
    • play sounds via `aplay` or similar, Ex: _2_aplay borked_sound.png
    • execute other julia programs, Ex: _2_julia my_gtk_thing.jl
    • change the terminal colors, Ex: _2_setterm -term linux -back red

Wrap up

That is a pretty solid foundation to get yourself started. You can add/extend this code with a whole lot of content and make a truly memorable experience. Good luck!

Definitely let me know in the comments whether you ended up using any of this, and how it worked out. Thanks!

Login or Register to Award Mageek XP if you enjoyed the submission!