Old but robust : A Simple IRC Server

A preamble : why?

Old technology has its drawbacks and its advantages. Many early messaging protocols are very non-friendly to users by today’s standards.
The Internet Relay Chat (IRC) protocol, especially, does not let you upload pictures. It does not let you voice call.
It was designed in the late 80’s and early 90’s to replace older instant messaging protocols.
Of course, back then, mass distribution of multimedia content was impossible and, as such, it is not a core feature of IRC.
Furthermore, IRC is account-free, so a single device can connect as one or many users, and sessions end when the device disconnects, without data carrying over to other devices.
It is possible, however, to synchronize user information in order for the information of extended features (NickServ nickname registration, Operator identification, etc) to carry over server-side.

Nonetheless, IRC is somewhat robust.
It’s old, but to this day, there is still actively developed software on both server-side and client-side to use it, on just about any platform you could think of.
A server for a couple dozens of people does not require a lot of computing power, or memory.
In general, IRC is a light-weight protocol that is easy on bandwidth (by today’s standards at least).
The protocol’s standard also describes security systems like channel passwords, server passwords, and moderation operations.
Supplementary services (like those mentioned above) can also be deployed easily to add user-friendly features like channel registration, nickname protection, and server audit.

If I’m saying a lot of things you don’t get, I recommend you read one of the many tutorials online that cover the basic operations of connecting.
I will probably have to write one myself, since, well, IRC is your typical “made by tech folks for tech folks” kind of technology, and people who tend to use it already know what’s up.

One structural aspect of the protocol can be particularly appealing : servers that run IRC-compliant software can interconnect with each other with appropriate credentials and form a network.
Within the network, a user can connect to any trusted server, securely, over SSL (the ‘S’ in ‘HTTPS’), and messages, channels, private messages, are shared between servers among the network.
When one server goes down, provided that it is not the only server connecting two halves of the network, only the users connected to that server leave, but the remainder of the network, messages, channels, remain intact.
And, finally, servers can be hosted by anyone with a server that stays on all the time and is reachable from the internet (even from home, although it is not recommended for big, professional networks).

Recent societal events have reminded me just how important it could be to have decentralized, user-run services, to which one can communicate with basic encryption and without relying on servers outside of our control.

So I decided I would use the knowledge I have with IRC servers and tell you how to install one easily and quickly (at least, as much as possible).

Infrastructure and installation

I will be assuming that you are running a server which is accessible on the internet.
Chances are, if you’re reading this, you can probably figure out how to do that at home or rent a server.
A lot of things will have to remain unexplained. I am trying to make this as easy to read for beginners as possible, but explaining more would turn this post into a 10,000 words long essay.
That server will be running a Linux-based distribution called Debian, with the current (as of writing this) stable version (number 10).
Your server probably does not need to match my distribution, since we will be compiling the IRC server.
Note that pre-compiled binaries are available for Windows.
Ideally, IRC servers should be online all day long.

Different implementations of the IRC protocol server-side have different additional features and vary a lot in terms of configuration.
This might be due to the tendency to build upon the current standard (and its extensions) to add more user-friendly features.
I will be using UnrealIRCd since it is the one I have most experience with.

Installing and compiling

Creating the IRC user

For security reasons, it is recommended that a user without administrator privileges download, compile and run the software.
The first thing we will do is create a system user called irc who is just a plain rando for the system.
I will also give this user a home in a particular place in the system. As an admin, run

useradd irc --home-dir /opt/irc
cp -r /etc/skel /opt/irc
passwd irc

The first command tells the system that a new user called irc with home directory /opt/irc should be created.
The second command copies a template for user home directories at /opt/irc. The last command gives a prompt to set the password for that user.

Switch to the irc user and move to its home directory.

su irc

Downloading the sources

We must then download the source code of the IRC server software.
The download page of UnrealIRCd provides some options, and I will be using the latest release (5.0 branch).

wget -c https://www.unrealircd.org/downloads/unrealircd-
tar zxvf unrealircd-

As indicated on the download page, you may want to check the integrity of the downloaded archive before opening it.
I have done so using the sha256 hash function implemented by sha256sum.


Next, you may want to open the UnrealIRC documentation alongside this page and check things out as we go.
Specifically, open the page called “Installing from source“, and proceed along with me.

Our layout will be as such : in /opt/irc/, the folder called unrealircd-5.0.<something> will contain the source code, and a folder called unrealircd will contain configuration files, binaries, and such.

Enter the source code folder and execute the configuration binary

cd unrealircd-

Press the space bar to skip the text.
Follow the indications of the configuration binary to set some important values that will be used to compile the IRC server, including :

  • Potential previous installations (we’ll leave it empty)
  • Installation directory (keep the default value, i.e. /opt/irc/unrealircd)
  • File permissions for the installed files (0600 is fine, it means “only the person who owns this directory can read and write these files”)
  • Path to OpenSSL library (people who know what they’re doing use this; I don’t)
  • Inclusion of remote configuration files, which is useless in our case.
  • Prefixes for channel administrators, owners and such. This is useful because it can help you tell apart who operates, moderates and controls a channel.
  • Nickname history length
  • Number of sockets (and file descriptors). The automatic setting is good.
  • Any other custom parameters

When you approve the last one, a series of tests should fly by, eventually telling you if something is missing.
Note that you will at least need to install a C compiler like the Gnu C Compiler (GCC), as well as the Binary Utilities (binutils) suite (all of them available using standard package managers).

When tests and module compilation is done, the questions resume, asking us if we want to use a SSL certificate.
This is more or less mandatory, since the IRC protocol does not require the implementation of specific encryption mechanisms between the server and the client, or between servers.
Without this encryption, sensitive content, like message content (including potential passwords and sensitive information) and aggregation information (shared between servers) would then be sent unencrypted and would be trivial to gather for anyone familiar with packet sniffing.

The prompts for the SSL certificate generation are very easy to understand.
Note that it does not matter what “Common name” (or “Fully Qualified Domain Name (FQDN)“) you use since this is a self signed certificate (even if you have a FQDN, you’d have to issue a certificate verification request).
You will have to tell your users to trust the certificate, since it will be marked as invalid, because no certificate authority has verified its validity.
In order to get a valid, certified ssl certificate (note that it requires a FQDN point to your server), you might want to check out Let’s Encrypt.

When this is done, the configuration binary tells you its final, ominous words : “Type ‘make’ and let it compile”. Do so

make -j3
make install

The -jX flag (essentially) tells make to split tasks onto X different processor cores.
This can be ignored, but with most computers having many cores nowadays, being able to cut your compilation time by two or three is good to take.
The second command copies everything that was compiled by the first one into a new folder, /opt/irc/unrealircd.

Configuring the IRC daemon

Configuring an IRC server is a tough job. With normal setups, you’d likely want to spend a lot of time reading the complete configuration page. We don’t really have that time.

Working with the example configuration

Thankfully, as described in the installation guide, a standard configuration ships with UnrealIRC.
It only needs some tweaking. We’ll get started with that.

cd /opt/irc/unrealircd

The directory conf contains configuration files.
You can use the example file conf/examples/example.conf and copy it to become the main configuration file.

cd conf/examples/example.conf conf/unrealircd.conf

Reading the syntax guide will help if you’re not used to this style of block-based configuration.

Open the file with your favourite editor, and let’s walk through it. A lot of it is just default stuff required by the IRC protocol.
Other blocks define important informaiton we need to change.

A note on configuration

Some things in the configuration of a server are unique to it. Others must be common within a network.
I will configure a server at address irc.example.com, with standard ports 6667 (unencrypted), 6697 (encrypted), 6900 (server exchange).
It will belong to a network called ExampleIRC. It will have server ID 001.

My main administrator will be nicknamed Malo, with username malo and host

The ME block

The ME block describes basic information about one server within the network.
Here is the documentation for that block.
We will set the following values :

  • name is the name of the server within the network. It servers to identify the server within the network and, ideally, it is also its address.
  • info should be a string describing the network’s name, and should be common to all servers in the network.
  • sid is the server identifier, which should be a unique number for every server in the network.

In our case, we end up with the following me block.

me {
name "irc.example.com";
info "ExampleIRC";
sid "001";

The ADMIN block

The ADMIN block is described here. It gives basic information about the administrator of the server.
This is just meant for users to be able to reach out to administrators, and can be set to anything if you wish for more.. anonimity.

admin {
"Malo Anonymous";

The CLASS blocks

Entities that will connect to the server are described by CLASS blocks. Each one of them describes a class, or groupe of users with different network restrictions and permissions.
The default configuration defines a class for standard human users (clients), one for human operators (opers, more on that later) and one for server entities, typically other servers in the network (servers).

The default config is fine.

The ALLOW block

The ALLOW block describes connection permissions for every one of the classes defined before.
It might be useful to familiarize yourself with the concept of wildcard characters and the typical user identification pattern, or ‘mask’ :


In my case, I allowed devices connecting from my local network (addresses in 10.8.X.X) to have 20 different connections.

allow {
ip *@10.8.*;
class clients;
maxperip 20;

The OPER block

IRC servers can benefit from some moderation tools. It is possible to create multiple levels of administrators and moderators.
Their privileges and exact authorizations are defined in another file (conf/operclass.default.conf to be specific), but, generally, operators can move users into channels, kick them from the server, or ban them from the server.
They’re the second degree of server moderation after channel operators, which we may talk about when discussing services.

Every IRC operator has a line of information that defines their name, what ‘mask’ they must match, what class of administrator they belong to, and a password.
Everything is explained here.

In our case, we would have

oper Malo {
class opers;
mask *@*;
//password "never write passwords plaintext";
password "$argon2id$v=19$m=8192,t=3,p=2$dr/J9YWaAcOaETsbAxIEGQ$iAv3cnU4tTzDgPjI2m7CzFzIy4iZeyB7enZqNtz3+aA";
operclass admin;
swhois "is an Administrator";
vhost admin.example.com;

After the operator has successfully obtained their privileges using the /oper command, whenever users will query information about them, the line Malo is an Administrator will be added, and their IP will be replaced by the “virtual” host admin.example.com.

If you want to be at least somewhat secure in your installation, use the unrealircd binary to get a hashed version of your operator password, and paste it where the “test” string is.

./unrealircd mkpasswd

After entering the password, you’ll get a string starting with $argon2id$v= as shown above.
This is what will be written in the file instead of your password.

The LISTEN block

In order for the server to be reachable, it must know what port to listen for incoming connections on.
The LISTEN block defines the different ports that will be listened on.
Typically, port 6667 is used for non encrypted communication, and port 6697 is used for encrypted communication.
However, a lot of internet firewalls do not allow ports outside of the standard 80 and 443 (web and encrypted web) to be used, so if you happen to need to change that, and you know what you are doing, this is where you change it.

// No options = no encryption, accept human clients
listen {
ip *;
port 6667;

// Option TLS = encryption, accept human clients
listen {
ip *;
port 6697;
options { tls; }

// Options TLS and serversonly = encryption, and only other servers
// in the network can use this port to exchange information
listen {
ip *;
port 6900;
options { tls; serversonly; }

Notice how we did not restrict which IP address could access what port.
It is possible to do so if you want to, by replacing the wild card character by an expression like 10.8.*.

An IRC network is made of multiple servers, but they need to know who they are, and be sure they are connecting and exchanging with a legitimate peer.
This is the purpose behind the LINK block. A server has as many LINK blocks as it does connections
to other servers. Typically, you will want to have linked servers use the same kind of IRC daemon, or, at least, know how to make different ones function.

Comment both examples with // comment markers at the beginning of each line, for now.

The ULINE block

Historically, the configuration files for IRC daemons were simple text files where one single line contained a full instruction of configuration, and began with a
letter identifying it. The jargon of IRC kept shadows of that outdated configuration system in the name of some rules (K-Line, G-Line, U-Line, etc).

The ULINE block tells your server that other servers should have more powers, and give them orders.
This is usually the case of IRC services, which run by pretending to be servers and communicating with other servers.

This is useless for now but keep it in mind when you want to set up services.

The DRPASS block

One of the privileges enjoyed by administrators and some IRC operators is the ability to request for the server to restart or stop (in tech linguo, ‘die’).
The /restart and /die IRC commands are designed to do so, and they each take a password that is set in the DRPASS block.

drpass {
restart "again, never put passwords";
die "in plain text like that";

Note that you can use hashed passwords like before, so that if your configuration file is accessed by malicious individuals, you’re not (completely) dead in the water.

The LOG block

Server administrators often use logs to diagnose issues and monitor the well being of the services they run.
The LOG block tells our server what to write and where to keep track of what it is doing.

The default configuration is fine.

The BAN block

These are pretty explicit. They are meant to prevent someone (or something) from connecting if it is a known malicious actor.
The entity trying to connect is matched with a pattern, and banned if it matches, with a given reason.

These BAN blocks work for nicknames, users, IP addresses, versions, real names, and servers.
They all descend from the Z, G, K and GZ lines of olden IRC configurations.
When a users needs to be banned, IRC operators will usually run /kline or /gline rather than add lines in there.

It is recommended to keep the ban of any user attempting to impersonate ChanServ in place. All others can be commented.

Blocks underneath describe exceptions to these bans, for testing purposes or for legitimate exceptions.

Other blocks

One particularly obscure feature of IRC is the set of actions known as Direct Client-to-Client (DCC). It actually allows client to share files, chat, and request information on each other without having to rely on the server or network they are connected to.

Some rules can be put in place to block some DCC features, like file sending (typically to stop bots from spreading virus files).
It is also possible to deny things to users, like channel names, client versions, or server links. This is done with a DENY block.

Users can acquire “virtual hostnames” or “vhosts” that are meant to mask their real IP address using some string of text (nowadays, most ISPs will provide a host name that sort of hides it, but still).
Typically, operators get one to hide their IP address.
Virtual hosts are protected using a login and a password, so, for example, someone whose original mask matches *@some-reverse-proxy.net can execute /vhost cat goes-meow to replace that host name with very.cool.hostname.

vhost {
vhost very.cool.hostname;
mask *@some-reverse-proxy.net;
login "cat";
password "goes-meow"; // Again, don't use plain text passwords

UnrealIRC has a feature where the server can ask a DNS blacklist if an IP or domain name is known for some kind of activity.
Some of there blacklists record Tor output nodes, open proxies, drones / spammers, attackers, and so on.
I have never used it, and I don’t know what side effects it may have. I will disable it.

The SET block

Finally, we reach some important blocks that give general but detailed information on the server.
The SET block is particularly complicated to configure, but the example provided works fine.
The following values are set :

  • network-name must be identical for all servers in the network
  • default-server is the name of the default server in our network
  • services-server is the name of the services server, which we haven’t deployed (yet?)
  • stats-server can be left out when there is no statistics bot
  • help-channel is the default channel where help can be sought
  • hiddenhost-prefix is a prefix of three or four letters used to hide the host name of users who request it (and clients nowadays usually do). It should be common to the network in order to hostname bans to work.
  • prefix-quit is the string shown to users when a person disconnects from the server.
  • cloak-keys are three random chains of characters used to derive pseudo-random hostnames that hide users’ real host name. Follow the comments to set these.
  • kline-address is a URL/email address shown to users upon being banned. It’s typically meant to receave appeals or explain reasons they were banned.
  • modes-on-connect is the modes a user receives whenever connecting. Note that +x, or “cloak hostname” is therefore enabled by default.
  • modes-on-oper same as modes-on-connect for people who become IRC operators.
  • modes-on-join same but for people who “join” channels. The meaning of all these modes is found here.

In my example, we will have

set {
network-name "ExampleIRC";
default-server "irc.example.com";
//services-server "services.example.com";
help-channel "#help";
hiddenhost-prefix "ExI";
prefix-quit "Quit";
cloak-keys {

The remainder of the settings are fine by default and relate to user control and quality of service matters.

Running the server

Assuming everything was configured properly, and your ports are available to the global internet, you can now try and run the server.
As irc, tell the server to start.

./unrealircd start

Statistically you’ll have messed up something with the configuration. The information displayed on screen will tell you what and where.
If everything works, the binary will say “UnrealIRCd started” and give you back the prompt. This is normal.
The server is running. You can verify by using an IRC client and connecting to it using your address and the port you defined.

More things to set, still

Right now you have a very bare bones server. It is functional, but it is not very handy. A lot of things have to be changed.

  • The default operator we created above cannot stop or restart the servers, only update the configuration (using /rehash). See the operclass privilege list or informations on what privileges to add or what class to use.
  • The server has no “Message of the Day” file (located at conf/ircd.motd), which shows users a greeting message
  • There are no user services. Channels will cease to exist alongside all the information (modes, passwords, descriptions) surrounding them.
  • The server needs to be restart manually in case of crash, or when you reboot the computer.
    And many other things.

Even though it is bare bones, it will function as a boilerplate for you to set up everything else. I’d recommend reading the security page on secure practices as well.

Changing the lone operator’s permissions

We want our only operator to have a lot more control over the server, and, especially, to be able to kill and restart it.

We will change the admin operator class to allow every admin to use /die and /restart. This may not be the best solution long term, but with a real infrastructure, you will be able to make better decisions regarding what classes get what privileges. Open conf/operclass.default.conf.

operclass admin {
permissions {
channel { operonly; see; override { flood; } }
self { getbaddcc; opermodes; set; }
server { opermotd; info; close; module; dns; rehash;
remote; description; addmotd;
addomotd; tsctl { view; } }

Adding permissions::server::die and permissions::server::restart allows admins to use both commands shown above.

Enabling automatic restart

Ideally, if you server is meant to run while you are not watching over it, you would want it to restart when the computer does as well. Under a standard stable Debian system, this can be done using CRON (whichever implementation happens to be installed on your system). Add the following line to your cron file (run crontab -e and chose an editor) :

@reboot        /opt/irc/unrealircd/unrealircd start

And voilà. Whenever the server reboots, the server will restart!

Conclusion.. not really

However documented this post is, it is nowhere close to covering everything I wanted to cover. It is extremely basic, and you only get a boiler plate server that is just functional enough for basic moderation, and chat. Users who will do more than simply chatting will have to learn about the basics of IRC, channel and user modes, simple moderation commands, etc. People who are brave enough to follow this and have questions can throw me an email using the icon down at the bottom of this page.

I don’t know how useful this will be. I don’t know what people will want to use IRC for, if at all. Deploying a functional server is a fun project. You can learn a lot of things if you are a hobbyist sysadmin like I was when I first worked through it all (and still am today).

All I ask is that you use this knowledge for good, for your communities, for your people.

New server. New system. New PostgreSQL database

A few days ago, I launched a website called “Vulpine Citrus”, meant to act as my front end with the rest of the world. Before that, I used to run a lot of my personal services on a server shared with a friend. After I bought my domain name, and VPS, I decided to begin moving my services onto my new server and stop hogging the storage space on that other, shared system.

I had, until then, gracefully escaped anything vaguely technically about managing database systems. I still am far from understanding the deep, underlying concepts of DB managers. However, I had an entire computer science semester of “Databases 101” last year, and I felt a little more enclined to start and play around with the PostgreSQL database system.

I will show you how to dump and export databases from one cluster to another on different machines. Both systems will be running Debian 9 or Debian 10. As far as my understanding goes, and that is all you need to understand this article, a cluster is a single instance of postgreSQL running on a system, with multiple clusters able to run at the same time, using different versions of the software.

On the former host of my database, I was running PostgreSQL 9.6. My cluster was called “9.6 main”. On the new system, I am running PostgreSQL 11. My cluster is called “11 main”. I will only be exporting one database, and creating new users. If you wish to dump and export a whole cluster, you will have to find someone more competent.

First off, logged into my new system, I had to install, enable and start postgresql 11

~# apt install postgresql
~# systemctl enable postgresql
~# systemctl start postgresql

You can check whether or not the server is running by using

~# pg_lsclusters

Which should show something similar to

Ver Cluster Port Status Owner    Data directory              Log file
11 main 5432 online postgres /var/lib/postgresql/11/main /var/log/postgresql/postgresql-11-main.log

Now, head off to the old system. Log in as the owner of the database you wish to export (or any account capable of reading the database, really).

You can export the old database by using pg_dump.

~# pg_dump -U owner_of_database name_of_database > my_database.pgsql

What you’re doing, essentially, is generating a PostgreSQL script capable of creating a carbon copy of your database as seen by user owner_of_database.

You can copy that script by any mean to the new system. I personally use scp over ssh.

Back to the new system, you will have to create your (empty) database and the role associated to the user(s) that will access it. The tools called createuser and createdb do the job fantastically, such that you don’t need to write a single line of SQL.

~# createuser --interactive --pwprompt

You can tell PostgreSQL that your new user can create databases, or not, roles, or not, it depends on what kind of user you want. Mine is a simple account meant to just read and write the database without doing anything complex or risky with its structure, or any other database.

The proper database can be created using

~# createdb my_owner_name -O my_owner_name
~# createdb my_database_name -O my_owner_name

The new user will most probably require a database bear their name. When you log into the server without providing a database, this is where you end up. It does not fulfill any other purpose, but I like to create it as well.

Once everything is in place, and you can successfully log in using psql -U my_owner_name (or if you can’t because there are no databases), create the carbon copy of your former database by executing the dumped script

~# psql -U my_owner_name my_database_name < dump.pgsql

If everything goes smoothly, you should only see a lot of PostgreSQL instructions fly by in the command line. When the database is created, you can try and log into it. Check whether you can successfully log in

~# psql -U my_owner_name my_database_name

Once you are sure that everything was transferred as you wanted, you can go back to your previous system, and, optionally, clean up the database(s) you exported.

~# dropdb my_database_name

And you’ll be good to go! 🦊

The Vulpine Citrus

This is the official launch of Vulpine Citrus, my own personal web site! Whenever I find something interesting to talk about, or want to show a cool thing I’ve learned, it will end up here.

Baby's first BrainF*ck clone : The Moostar Programming Language

A few years ago, my journey into coding led me to the dark alleys of GNU Assembly. I quickly realized that the difficulties of Assembly made it almost impossible for me to use it in my daily coding life.

When it takes you three hours to reimplement strlen correctly, and you’re used to processing natural strings of characters, you may begin to feel like this is not the language for you.

Metwo years ago

On my way up from that dark pit of black magic I encountered a curious little fella I had heard of many years prior : BrainFuck.

BrainFuck is Turing-Complete and while it could theoretically let us implement any and all computable problem… BrainFuck may not be the best fit for most complex purposes.

I mean, just look at this hello world.


Of course, I was not content with simply making an interpreter. I started Moostar with the intent of making an interpreter, but quickly decided to add one features to standard brainfuck that I found gave it more power : the ability to creature functions, along with the inclusion of many fundamental arithmetic operations right into the interpreter.

For example, here’s how you can write two multiplications in Moostar.


The hexadecimal dump of the output should reveal a single byte at value 0x08.

Currently, Moostar implements all of the following standard subroutines.

/* 'this' is the object representing the interpreter */
this->function_register["sub_5"] = "-----";
this->function_register["sub_10"] = "----------";
this->function_register["add_5"] = "+++++";
this->function_register["add_10"] = "++++++++++";
this->function_register["imove"] = "[->+<]";
this->function_register["dmove"] = "[-<+>]";
this->function_register["clean"] = "[-]";

std::string scan8;
for (size_t i = 0; i < 8; i++)
for (size_t i = 0; i < 8; i++)
this->function_register["scan8"] = scan8;
this->function_register["scan16"] = scan8.substr(0,16) + scan8.substr(0,16) + scan8.substr(16,8) + scan8.substr(16,8);

this->function_register["foreach_cpy"] = ">[>[->+<<<+>>]>[-<+>]<<-]>[-]<<";
this->function_register["add"] = ">[-<+>]<"; // :R:X:
this->function_register["sub"] = ">[-<->]<"; // :R:Y:
this->function_register["mul"] = this->function_register["foreach_cpy"];

this->function_register["pow"] = ">>>+<<[>>[->+<]<[->>>+>+<<<<]>>>>[-<<<<+>>>>]<<<~foreach_cpy;<<-]>>[-<<<+>>>]<[-]<<";
this->function_register["eq"] = "[->-<]+>[<[-]>[-]][-]<";
this->function_register["neq"] = "[->-<]>[[-]<+>][-]<}";
this->function_register["lt"] = ">[->+<]<[->+<]>+>+>>+<<<[->-[>]<<]>>>[<<[-]<<+>>>]>-<<[-]<[-]<";
this->function_register["gt"] = ">[->+<]>>>+<<<<[->+<]>>[-<-[<]>>]>[-<<[-]<]>[-<<<[-]<+>>]<<";
this->function_register["m@0"] = "^[-]\\";
/* Stuff */

(let’s all walk past that hilarious mistake of “interpreter”/“interpretor”, younger me’s English was more than dubious)

In general, one would declare a procedure thusly.


It is useful to add a comment alongside a function’s definition to denote the position of its parameters, results, and all the cells it will need for its processing. For example, see that comment at the end of a definition for ~power.


This comment indicates that once pow is called, it will compute X to the power of Y, require the four neighbouring cells to do so, and place the result in the cell we call it from.

Currently, Moostar doesn’t check for dependencies of functions, and one could write a program that would send the interpreter into an infinite loop of procedures calling each other.

Yet, there is more to Moostar than simply adding standard functions. As you might have noticed from the weird circumflex character in my last command, m@0, I extended the charset beyond the simple ability to declare procedures. Two characters were introduced beyond those used in standard BrainFuck, and those for procedural syntax.
The first such character, the circumflex accent, switches the interpreter into ‘Meta Tape’ mode, while the backslash switches the interpreter back into ‘Data Tape’ mode.

The ‘Meta Tape’, or ‘Meta Registers Tape’ is a set of cells containing and controlling data related to the machine’s function itself. Originally, they were designed to directly modify CPU registers during execution. Two seconds after I had that idea, when I realized it could easily be exploited to bug the heck out of Moostar, I slowed down and only implemented a control register for the data pointer’s position. Interestingly, a legacy name for that meta tape remains in the code : this->ioregs (for I/O registers).

Now, the pointer on the meta tape and the pointer on the data tape are not the same. Thus, by switching to the meta tape’s zero register, we can chose what cell we will land on when we switch back to data.
This is actually what the m@0 procedure does, by resetting the value in meta register 0 (data_pointer_pos) to 0, and switching back to the data tape.

You can find the source code for moostar on GitHub and Talmar.