What the services hold that must not leak, where each secret lives and how little each token may do, what the files strangers send can reach, and what the bots keep of people. How the jails are made: Install and Forge.
None is in this repository; each is made or put in place on the host. .gitignore keeps any *.env out.
| Secret | Where |
|---|---|
| ReiWimsy's Discord token, its GitHub token | discordbots: /usr/local/etc/reiwimsy.env (root's, 600) |
| Tracer's Discord token, its issues token | tracer: /usr/local/etc/tracer.env (root's, 600) |
| AccordWimsy's Forgejo token, its GitHub token | gitsync: /usr/local/etc/accordwimsy/accordwimsy.env (its user's, 600) |
Forgejo's SECRET_KEY, INTERNAL_TOKEN, JWT_SECRET |
gitscarlet: app.ini, made in place, never leaving the jail |
| Each tunnel's connector token | the file --token-file names, in gitedge, tracerweb and status |
| The Cloudflare API token | the file CLOUDFLARE_TOKEN_FILE names, on the host |
| Gatus's push token | the host: /usr/local/etc/status-scarletnx/push-header (root's, 600), made by dist/status/install.sh; the jail's gatus.yaml (root:www, 640); @PUSH_TOKEN@ in dist/status |
ReiWimsy's and Tracer's env files are read by rc.subr as root and exported to the program, which runs as its own user;
AccordWimsy reads its own, as its user. No token goes on a command line: AccordWimsy gives git its tokens in git's
environment (GIT_CONFIG_*), and push.sh gives curl the push token from its header file.
| Token | Of | What it may do |
|---|---|---|
ReiWimsy's GITHUB_TOKEN |
GitHub, fine-grained | public repositories, read-only |
Tracer's TRACER_ISSUES_TOKEN |
GitHub, fine-grained | the issues' repository alone: Issues, read and write |
AccordWimsy's GITHUB_TOKEN |
GitHub, fine-grained, of the organization | read-only: Contents, Issues, Pull requests, Metadata |
AccordWimsy's ACCORDWIMSY_FORGEJO_TOKEN |
the forge, its own user | write:repository, write:issue, write:organization, read:user |
| ReiWimsy's Discord invite | the server | View Channels, Send Messages, Send Messages in Threads, Embed Links, Read Message History, Manage Messages (its first round deletes the webhooks' cards) |
| Tracer's Discord invite | the server | View Channels, Send Messages, Send Messages in Threads, Embed Links, Read Message History |
ReiWimsy's gateway asks for the Message Content intent (it reads a message's text for the numbers it names); Tracer's asks for no privileged intent, and the reactions only when it files issues (Tracer). Tracer's application is no Public Bot.
Each program runs in a jail of its own, as its own user (Install). The bots listen on no
port. Tracer's bot writes its site into a folder that the web jail, tracerweb, mounts read-only: the jail that
answers the internet holds no token and cannot change the site. The forge's three jails are isolated: the one that
holds the GitHub token (gitsync) answers no one, the one that answers the internet (gitedge) holds no token, and
Forgejo (gitscarlet) reaches nothing but DNS; a guard stops the three at the first process, socket or file it never
learned (Forge).
Tracer reads the files strangers send. Each message's files are read by a worker, the bot run again as tracer --report (Tracer):
- its environment without the bot's settings and tokens (every
TRACER_key taken out); - within 1 GiB of memory, 120 s of processor, 600 s in all and 128 MiB a file, with no core file (a crash's core would hold what a stranger sent), and every descriptor it inherited but the standard three closed (the bot's gateway, its database);
- the bot itself not dumpable on Linux (
PR_SET_DUMPABLE), out of reach of ptrace and the debugging sysctls on FreeBSD (PROC_TRACE_CTL), so that a worker a file took over, though it runs as the same user, cannot read the bot's memory or environment; the bot does not start without it; - shut in its own folder before it reads any file (Capsicum's capability mode on FreeBSD, Landlock on Linux), so that it can write neither the site nor the database, nor read any other file; one that cannot reads nothing, and the bot takes no answer that does not say it was shut in;
- its answer checked before any file moves from the work folder into the site.
GitHub is asked from the bot's own process; the worker never holds the issues token.
The site is static: everything a log or a message says is escaped in the pages, and nginx
(dist/freebsd/tracer-nginx.conf) serves it with X-Content-Type-Options: nosniff, Referrer-Policy: no-referrer
and a Content-Security-Policy that lets a page load only the site's own images, videos, style and site.js, with no
form, no base and no other page framing it; a page the bot is writing (*.new) is not served. tracer-mcp is
read-only, reads the site's folder and never the database, refuses another site's Origin and a request over 1 MiB, and
searches in time linear in what it reads; nginx limits its requests by client and in all.
- ReiWimsy reads a message's text only for the numbers it names, sends GitHub only the numbers, and keeps no message, no text and no author: its state holds its replies' and cards' ids (ReiWimsy).
- Tracer keeps the sender's user id and the ids of the messages that lead to a report, for the deletion, the updates
and the hourly limit; the site and its JSON name no one. A log's home folders become
~, and so do the folders of a path outside them (a disk's, a drive's, Android's storage), its addresses[ip]and[email], and the crash report's dumps are left out; a picture loses its metadata and a video its place, time and camera (Tracer). - AccordWimsy copies what GitHub shows publicly, each issue and comment opening with its GitHub author, day and link.
- The status page keeps nothing of anyone.