Starting Project Moon Hermit

Lua, SSB
project moon hermit

project moon hermit

Yesterday, I mentioned on SSB that I was working on something, well, this is it. I’m launching Project Moon Hermit which is something that I mentioned a couple times in the past but never really tried out. Well, yesterday, I decided to test the idea out instead of doing actual work and much to my surprise, it started working.

What is project moon hermit?

It is a new SSB client of sorts. It is not exactly a client but a version of Lua 5.3 that contains an embedded SSB client inside it. It was built by picking sbotc by cel and grafting #lua into it.

Sample script running.

Sample script running.

The script that generated the output above is:

print("hello from " .. _VERSION)

local whoami, err = ssb:whoami()

if (not err) then
    print(string.format("you are %s", whoami.id))
else
    print(string.format("some error happened?! %s", err))
end

local result, err = ssb:latestSequence(whoami.id)

if (not err) then
    print(string.format("latest sequence is %d", result))
else
    print(string.format("some error happened?! %s", err))
end

At the moment, I consider this project a proof-of-concept. It is not ready for general use as I’ve implemented only muxrpc_read_async calls. There are other paths inside sbotc.c that I need to change besides hooking the error handling back into Lua.

Be aware that this is not a library, it is a full interpreter. It executed your lua script after connecting to the SSB Server.

Source Code

The current proof-of-concet is at moonhermit github repo.

Next steps

  • Refactor muxrpc_read_source, muxrpc_write_blob_add, muxrpc_write_sink, and muxrpc_duplex to work with Lua.
  • Refactor the error handling to feed back into Lua stack.
  • Refactor the args in the command line into something that makes more sense for this project.

How it works?

I basically changed main() in sbotc.c a lot to extract the parts that called into muxrpc into a separate function that I expose to the Lua environment via the Lua C API.

Lua is mostly a toolkit instead of a language. You’re supposed to customize it and tailor it to your needs. This project wants to create a scripting language for SSB.

Exposing muxrpc to Lua

// initialize SSB library
lua_pushcfunction(L, luamux);
lua_setglobal(L, "muxrpc");

Yep, that is it. There is a luamux() function in the C source code, we add it to Lua and expose it as a global called muxrpc with just two lines.

Making it friendlier

The moonhermit interpreter makes use of some Lua files (which will eventually be bundled inside the C source code) to create a friendlier API on top of the exposed muxrpc() function. It loads a file called prelude.lua before running your script. That file makes use of json.lua so it can convert to and from Lua tables into JSON. Lua has metatables and metamethods. You can think of them as JS prototypes and function overloading.

The prelude.lua assembles an empty table (think JS object) called ssb. It attaches a metatable to it (think JS prototype). This metatable has an __index key which is set to a function. This means that accessing a non existant property on this table will cause __index to be called. This returns a new function, much like JS closures being used as function factories. This new function is a wrapped call to muxrpc. Those two lines in the sample:

local whoami, err = ssb:whoami()
...
local result, err = ssb:latestSequence(whoami.id)

Are actually using Lua syntatic sugar — table:method("a") becomes method(table, "a") — that when called, returns a new function that is immediately invoked.

The actual __index entry in the metatable is:

__index = function(self, k)
    return function(s, ...)
        local res, err = false

        local ars = table.pack(...)

        if (#ars == 0) then
            ars = "[]"
        else
            ars = json.stringify({ars[1]})
        end 

        local status, retval = pcall(muxrpc, k, ars)

        if (status) then
            res = json.parse(retval)
            err = false
        else 
            res = false
            err = retval
        end 

        return res, err
    end 
end

In the end it is a clever way to create a proxy object. I never implemented whoami or latestsequence in Lua or C. Calling it like ssb:whoami() is transformed into pcall(muxrpc, "whoami", "[]") which is a lua protected call so that even if it errors out in the C code, we’re safe.

Also, you might have noticed that Lua has support for returning multiple values. That is used to return a result and an error at the same time, like SSB callbacks do.

Phew, I’ve written so much. It is because I’m excited about all of this. It is early days but I can see this growing into a very good way of creating simple SSB tools and scripts.

At the moment this has only been tested in Linux. It should work on a Mac too but you might need to tweak the Makefile. It doesn’t work on Windows because a lot of the C code relies on GNU stuff.


This wouldn’t be possible without all the amazing work by Cel. πŸ’–

PS: My C skills are garbage…

Did you like this post? Want to support me?

If you want to support me you can buy me a coffee at ko-fi.

Comments? Questions? Feedback?

You can reach out to me on Twitter, or Mastodon, Secure Scuttlebutt, or through WebMentions.

Mentions