castling.club source code, an ActivityPub chess bot
source link: https://www.tuicool.com/articles/hit/bueI3q6
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
♚ castling.club
This is the codebase for castling.club , an ActivityPub server with a single hardcoded 'King' service actor that acts as a chess arbiter.
This code is MIT-licensed, see LICENSE.md
.
Not a requirement, but if you use this code for anything, I'd love to hear about it! Send me a toot: @[email protected]
How to run
To run the app, you need Node.js (targeting 10.x) and PostgreSQL (targeting 9.6.x). Yarn is also recommended, but optional.
First, install the Node.js library dependencies:
# For development: yarn # For production: yarn --prod # Or use npm, if you don't have Yarn.
Next, you should configure the app.
Configuration is provided through environment variables, which can also be
provided in a .env
file. The defaults may work for development, but usually
need to be tweaked. The following variables are used (with their defaults):
-
APP_SCHEME="http"
The scheme used to access the app. The app only talks plain HTTP, but this should be set to
https
when behind a reverse proxy providing HTTPS. -
APP_DOMAIN="chess.test"
The domain used to access the app. You should set up a virtual host matching this, and have it proxy to the app.
-
APP_ADMIN_URL=""
Profile URL of the server admin, publicly accessible through the NodeInfo API.
-
APP_ADMIN_EMAIL=""
Email of the server admin, publicly accessible through the NodeInfo API.
-
APP_KEY_FILE="signing-key"
Filename of the RSA private key (in PEM format) used to sign activities for the 'King' actor. A matching file with a
.pub
suffix MUST be present, containing the RSA public key (also in PEM format).The default matches the
tools/gen-signing-key.sh
script, and should be fine in most cases. -
APP_HMAC_SECRET="INSECURE"
The secret used to sign public data generated by the app that must later be verified. The default MUST NOT ever be used in a public instance.
Currently, this is only used to sign image URLs, so that they cannot be tampered with.
One can be generated with, for example:
head -c 63 /dev/random | base64
-
NODE_ENV="development"
One of
development
orproduction
.When in
production
, the app will reject federation attempts not over HTTPS, or from origins that should not appear on the open Fediverse.This variable may also change various behaviour in library dependencies.
-
PORT="5080"
The TCP port the app will listen on for HTTP requests.
The app will always bind on all interfaces, and MUST be properly firewalled in a public instance.
-
PostgreSQL connection variables follow the standard set defined at: https://www.postgresql.org/docs/9.6/static/libpq-envars.html
Now generate the signing key:
./tools/gen-signing-key.sh
Make sure the database exists in PostgreSQL, then create the schema:
./tools/migrate.js up
The app can now be started with:
./server.js
Debug logging is provided by the debug module. For development, castling.club's own debug logging can be enabled using, for example:
DEBUG='chess:*' ./server.js
Testing interaction in development
With some work, it's possible to run local castling.club, Mastodon and Pleroma instances talking to each other. A simple setup might have:
chess.test mastodon.test pleroma.test
To get this working, you want a webserver configured with these virtual hosts
running in front, and correctly reverse proxying to each app. The hosts can then
be defined in your /etc/hosts
file, or a custom DNS server with a wildcard *.test
domain, to point at the local machine ( 127.0.0.1
/ ::1
).
Both Mastodon and Pleroma perform Webfinger requests over HTTPS. You can either setup HTTPS for all your local testing instances, or patch either project to use plain HTTP. ( Mastodon patch , Pleroma patch . You MUST NOT ever run such a patched codebase in a public instance.)
App structure
The server.js
entry point loads configuration, then starts the two main tasks
of the app:
src/front/ src/deliver/
These could easily be split into separate processes, and could also individually be scaled horizontally, because all state lives in PostgreSQL. In practice, that kind of scale is not reached, so things are kept simple and single-process for now.
The codebase heavily uses async/await, with the Koa framework for its frontend.
Instances of library dependencies and our own services (such as Koa, the
PostgreSQL client, etc.) live on an app
object. This structure can be best
seen in src/shared/createApp.js
and the index.js
files of each task
directory. This is basically poor-man's dependency injection; there's no
automated system to resolve the dependencies between objects, we just create
them in the order we know works.
The castling.club codebase sticks to ActivityPub naming. Notably, this means: not 'toots', but 'notes'.
When interacting with the bot, there are roughly 6 steps that take place:
- The user looks up the bot by name.
- The user publishes a note.
- The user's instance delivers it to the bot's and others' instances.
- The bot acts on the note and publishes a reply.
- We deliver the reply to the users' instances.
- The users views our reply.
Step 1 involves an optional Webfinger request, and fetching our Actor object.
Both of these are implemented in src/front/actor.js
. In a server
implementation like Mastodon, these would be dynamic, because there's an account
system. But in castling.club, a single account is simply hardcoded.
Step 3 is when we receive a signed request POST /inbox
. This is implemented in src/front/inbox.js
. The signature is verified by code in src/shared/signing.js
, then dispatched as an internal event noteCreated
.
Step 4 starts when the event is picked up in src/front/dispatch.js
. Here, the
note is parsed and methods are called on src/front/game.js
and src/front/challengeBoard.js
, which are roughly our controllers when thinking
in MVC. When these decide a reply needs to be sent, they call createObject()
of src/front/outbox.js
, which queues deliveries to user inboxes.
Step 5 is where outbox deliveries are picked up, implemented in src/deliver/deliver.js
. Each delivery is a two-step process, where first the
addressed Actor is fetched to discover the inbox, then the note is actually
delivered to the inbox. These are implemented as essentially two separate jobs
folded into a single table deliveries
. Keeping these separate allows combining
deliveries that have the same shared inbox.
Step 6 is of note, because images are lazily rendered. A signed image URL is
generated in step 4, and when requested, the image is fetched from cache or
rendered on the spot. This code lives in src/front/draw.js
.
The ActivityPub objects are all accessible by browser as HTML. This is
important, because the actor URL and note URLs are often visible to the user.
Castling.club renders templates for these when accessed through a browser, as
well as providing various other browser-only pages in src/front/misc.js
. The
actual template files are EJS
files, found in assets/tmpl/
.
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK