マリウス . Teaching an Old Dog New Tricks: Forgejo + XMPP
Forgejo only lets people register with an email address, so I bent it a<br>little bit to make it send XMPP messages and let users sign up with a JID<br>instead.
I recently decided to leave Codeberg and run my own Forgejo instance<br>instead. After finishing the initial setup, I realized that a forge I host<br>myself is a forge that I get to shape, and that I no longer have to settle for<br>whatever feature set someone else considered reasonable. I can now make it truly<br>mine, and switch on the things I have always wanted a Git host to do.<br>The first of those things concerns the registration. Right now Forgejo, like<br>pretty much every other forge, wants an email address and sends a verification<br>link to it, because apparently a clicked link is still considered a proof that a<br>human is on the other end. I, however, would much rather let people sign up with<br>an XMPP JID instead. Partly, because an email address you can receive a link<br>at has long stopped being a real obstacle for automated spam, so the whole email<br>verification is less beneficial than people assume, and partly because XMPP is<br>the superior protocol, and I would prefer my users to arrive over it rather than<br>over email to begin with.<br>Unfortunately, Forgejo has no native XMPP support. Additionally, it also<br>makes no use of Go’s shared objects, which would let extensions be built<br>largely independently of the core, and which for a monolith of this size would<br>make a great deal of sense. There is no clean place to add a protocol to it<br>without going into the core itself.<br>I was not, however, looking to implement real JID registration and<br>authentication into Forgejo. That would have been a fight against<br>windmills, and I say that from experience. My past attempts at far lighter<br>changes were killed off by bureaucratic requirements like<br>design discussions and collecting of use cases, and probably<br>a laissez-passer A38 somewhere along the way. So I did what any<br>reasonable but slightly unhinged person would do, and decided to bridge<br>Forgejo’s existing email integration into XMPP instead.<br>Introducing Switchyard<br>Switchyard is a small daemon that speaks SMTP on one side and<br>XMPP on the other. Email and XMPP happen to share the same address form,<br>user@host.tld, so the recipient maps across directly, and a message addressed<br>to hello@example.com goes out as a chat to the JID hello@example.com. It<br>accepts the mail a service submits over SMTP, turns each one into a job on a<br>queue, and a worker delivers it over a standing XMPP connection that<br>reconnects on its own when the server drops it.<br>Because it listens for SMTP the way any mail server does, Forgejo needs no<br>patch to hand its mail over. It gets configured as the mailer endpoint with<br>the same block you would point at a real mail server:<br>[mailer]<br>ENABLED = true<br>FROM = forgejo@myhost.tld<br>PROTOCOL = smtp<br>SMTP_ADDR = 127.0.0.1<br>SMTP_PORT = 587<br>USER = forgejo<br>PASSWD = `change-me`
On the other end, a short switchyard.toml gives it the credentials it checks<br>Forgejo against, a certificate for the submission port, and the single XMPP<br>account it sends everything from:<br>[redis]<br>addrs = ["127.0.0.1:6379"]
[smtp]<br>allowed_ips = ["127.0.0.1/32"]<br>username = "forgejo"<br>password = "change-me"
[smtp.tls]<br>enable = true<br>cert = "/etc/switchyard/tls/cert.pem"<br>key = "/etc/switchyard/tls/key.pem"
[xmpp]<br>server = "xmpp.myhost.tld:5222"<br>username = "switchyard@myhost.tld"<br>password = "change-me"
With that in place, every message Forgejo would otherwise send by email, like<br>the sign-up confirmation, the password reset, or the note about a new issue,<br>instead goes out over XMPP to whatever JID the user typed into the email<br>field. Switchyard also handles IDNs, because, you know, that’s kind of my<br>thing. :-)<br>SMTP always carries the domain in its punycode form, while XMPP uses the<br>native IDN, so it decodes the recipient domain on the way through, and a user<br>who registers as user@xn--gckvb8fzb.com in Forgejo is reached at<br>user@マリウス.com on XMPP.<br>Beyond Forgejo<br>This lightweight service is not specific to Forgejo. Switchyard works with<br>anything that can be pointed at an SMTP server, and turns that software into<br>something that speaks XMPP without the software itself knowing that anything<br>changed. The one adjustment that should be made on the other side is relabeling<br>the “Email address” field in the HTML templates to “JID”, so people know<br>what to put there. In Forgejo’s case that is a small template patch, which you<br>can easily maintain yourself. It’s a very different commitment from maintaining<br>a full XMPP implementation that upstream will most likely never accept.<br>The downside is that this costs you email (for now!). Once Forgejo is talking<br>to Switchyard, everything it sends goes to XMPP, and a user who did enter an<br>email address gets nothing. I’m already working on fixing that, by having<br>Switchyard first check whether the destination host is an actual XMPP...