~technomancy/pengbot

An IRC bot written in Fennel
Reload versions when reiniting.
Fix a bug where one-line? wasn't being applied to data.
Bump lua version to 5.4.7.

clone

read-only
https://git.sr.ht/~technomancy/pengbot
read/write
git@git.sr.ht:~technomancy/pengbot

You can also use your local clone with git send-email.

#P'eng bot

In the northern ocean there is a fish, called the k'un, I do not know how many thousand li in size. This k'un changes into a bird, called the p'eng. Its back is I do not know how many thousand li in breadth. When it is moved, it flies, its wings obscuring the sky like clouds.

When on a voyage, this bird prepares to start for the Southern Ocean, the Celestial Lake. And in the Records of Marvels we read that when the p'eng flies southwards, the water is smitten for a space of three thousand li around, while the bird itself mounts upon a great wind to a height of ninety thousand li, for a flight of six months' duration.

  • Chuang Tzu

An IRC bot written in Fennel.

#Usage

$ make pengbot
$ ./pengbot irc.libera.chat 6667 my-pengbot "#pengbot" "#otherchan"

Don't worry if you see it write out an error message that says fatal: destination path 'checkout' already exists and is not an empty directory.; this comes from git initializing the checkout of Fennel.

Note that this does not use TLS to connect; it should not be used with any sensitive data.

Commands can be addressed directly to the bot's nick. In the first given channel, the bot will also listen to commands prefixed with a comma.

Run ,help to get a list of commands. The main commands are:

  • ,set TERM DEFINITION: store a term and its definition
  • ,get TERM: retrieve a term's definition
  • ,forget TERM: delete a term's definition
  • ,eval FORM: evaluate a piece of Fennel code and print return value
  • ,compile FORM: print the output from compiling a piece of code
  • ,multi-eval VERSION FORM: run a piece of code in a specific Fennel version
  • ,multi-compile VERSION FORM: compile in a specific Fennel version
  • ,versions: list available versions of Fennel
  • ,reinit VERSION: reset environment for a specific Fennel version
  • ,pull: pull the latest versions from Fennel's git repository

Pengbot can run code against arbitrary versions of Fennel in order to demonstrate code in IRC channels against old versions, and how things have changed over time. the ,pull command allows you to bring in new versions which may not have even existed when Pengbot was launched!

It stores dictionary data (from set command) in the data/ directory that it's launched from. No limits are in place to prevent it from filling up the disk yet, so disable that command if you're worried.

Requirements: Lua 5.3/5.4/luajit and luasocket.

#Implementation

Pengbot is written in a somewhat experimental style based on capabilities. The entry point (main.fnl) and capabilities.fnl file are run with full access to the entire system, and the rest of the program is run in a restricted environment where the only access to the outside world comes from capabilities that are passed around from one function to the next.

Beyond that, inside the restricted environment, input which comes in from users over IRC is run in an even more restricted environment, this time even isolated to the specific version of Fennel that the user requested.

This serves a kind of dual purpose; the most obvious is that the abolition of ambient authority makes the system dramatically more secure even in the face of serious bugs. An attacker who finds a remote-code-execution flaw in the code which handles user input will only gain access to the capabilities needed by the function which they are able to compromise. In the worst case scenario, this would be the caps.os and caps.io tables, which contain functions capable of running only a very limited set of commands defined in main.fnl or functions for writing to the disk, but only for paths described in main.fnl.

The second purpose is as a sort of organizational tool. Because there is no ambient authority in the code inside the outer environment, it's easy to tell the level of access of a function just by looking at its argument list. If it takes conn then it can do basically anything (within the overall capability restrictions of the application still; it can't execute arbitrary commands) but only a small number of functions take this argument. Likewise if it takes caps then it can access any outside capability, but much more common are functions which take only a single capability, or functions which take none of the above and can be assumed to either be pure functions or functions which only make changes to tables.

This helps you see at a glance what kind of function you're looking at, greatly aiding the ability to orient yourself quickly in any file.

#License

Copyright © 2018-2023 Phil Hagelberg and contributors

Distributed under the GNU General Public License version 3 or later; see file LICENSE.