Mastodon API - reference social.
  • Ada 62.7%
  • Python 34.6%
  • JavaScript 2%
  • CSS 0.6%
Find a file
LowEel f268f6c073
All checks were successful
continuous-integration/drone/push Build is passing
Fix Alire dependency fetches in Docker builds
2026-09-02 21:17:38 +02:00
alire Initial commit 2026-08-19 21:58:25 +02:00
doc Support secure-mode ActivityPub signatures 2026-08-29 12:13:30 +02:00
docs Initial commit 2026-08-19 21:58:25 +02:00
etc Initial commit 2026-08-19 21:58:25 +02:00
smoketest Fix Alire dependency fetches in Docker builds 2026-09-02 21:17:38 +02:00
spark Treat bounded HTTP errors as valid outcomes 2026-09-02 19:11:00 +02:00
src Log recovered filesystem contention as debug 2026-09-02 19:41:43 +02:00
static Implement probe-based opaque instance quarantine 2026-09-01 22:29:32 +02:00
tests Initial commit 2026-08-19 21:58:25 +02:00
tools Optimize uploaded images and isolate keystore I/O 2026-08-30 20:44:50 +02:00
.dockerignore Optimize uploaded images and isolate keystore I/O 2026-08-30 20:44:50 +02:00
.drone.yml Combine native architecture pipelines [CI SKIP] 2026-09-01 11:30:25 +02:00
.gitignore Initial commit 2026-08-19 21:58:25 +02:00
alire.toml Initial commit 2026-08-19 21:58:25 +02:00
DOCKER.md Initial commit 2026-08-19 21:58:25 +02:00
Dockerfile.amd64 Fix Alire dependency fetches in Docker builds 2026-09-02 21:17:38 +02:00
Dockerfile.arm64 Fix Alire dependency fetches in Docker builds 2026-09-02 21:17:38 +02:00
Dockerfile.builder.amd64 Fix Alire dependency fetches in Docker builds 2026-09-02 21:17:38 +02:00
Dockerfile.builder.arm64 Fix Alire dependency fetches in Docker builds 2026-09-02 21:17:38 +02:00
Dockerfile.builder.spark Initial commit 2026-08-19 21:58:25 +02:00
Dockerfile.spark Route filesystem access through canonical boundaries 2026-08-22 22:40:45 +02:00
INSTALL.md Initial commit 2026-08-19 21:58:25 +02:00
LICENSE.md Initial commit 2026-08-19 21:58:25 +02:00
README.md Advance Xenomorph to chestburster 2026-08-21 00:35:44 +02:00
spark_helpers.gpr Harden persistent ActivityPub payload handling 2026-09-02 18:09:17 +02:00
xenomorph.gpr Split queue responsibilities and add group federation 2026-09-01 02:02:16 +02:00

Xenomorph

Xenomorph is a usable ActivityPub server written in Ada.

At this stage it starts as a source-guided port of snac2, especially for the visible interfaces and filesystem behavior. The long-term goal is not to pretend that Ada is C, but to carry the same small-server idea into a codebase with stronger type safety, better modularity, and a maintenance model that fits exposed Internet software.

The short version is: Daddy is Snac.

Why

snac2 is fascinating because it proves that a single-person project can still implement a practical ActivityPub server without turning into a large platform. ActivityPub federation is difficult, and the protocol often feels much more complicated than it needs to be. snac2 approaches that problem with discipline: plain storage, small moving parts, and a bias toward direct, understandable behavior.

Xenomorph exists because that approach deserves an Ada implementation.

This is not a claim that snac2 has specific security problems. It is a different engineering bet: C can be used extremely well by expert programmers, but Internet-facing C services also live close to classes of memory and data handling bugs that Ada is designed to make harder. Xenomorph tries to keep the practical minimalism of snac2 while gaining Ada type and data safety.

Also, it is fun.

Goals

  • Keep the visible behavior close to snac2 while the port is young.
  • Use snac2 as the design map: simple filesystem storage, simple processes, clear HTTP behavior, and as little machinery as possible.
  • Avoid a database. The filesystem is the storage model.
  • Keep configuration transparent and inspectable.
  • Make the project suitable for small Docker deployments.
  • Fit homelab-scale servers, including very small machines and NFS-backed storage.
  • Stay aligned with snac2 development where that makes sense.
  • Eventually let Xenomorph grow its own features once the port has legs.

Current Status

Current version: Xenomorph/chestburster.

Xenomorph is now usable for real small-instance testing and everyday dogfooding, but it is still hardening and should not yet be treated as a conservative production recommendation.

The project currently has working ActivityPub federation for the implemented S2S flows, filesystem storage, Web UI work, WebFinger and actor output, login/admin scaffolding, and the subset of the Mastodon client API implemented by snac2. Comparative smoke tests exercise Xenomorph and a real snac2 server side by side.

If this sounds interesting, follow the project and expect the feature list and release notes to become useful as the implementation matures.

Implemented So Far

The implemented list is intentionally conservative. It should only describe what exists in the code today.

  • Ada project built with Alire.
  • Filesystem-based server and user storage.
  • User creation with local key material.
  • WebFinger responses for local users.
  • ActivityPub actor endpoint with local profile data.
  • Web login and a Semantic UI client for home, federated and hashtag timelines, notifications, conversations, profiles, people, follow requests, bookmarks, pinned posts, followed hashtags, lists, and search.
  • The Web UI uses locally vendored Atkinson Hyperlegible Mono by default.
  • Web composer and status controls for replies, media, content warnings, polls, scheduling, visibility, language, likes, reposts, bookmarks, pins, edits, deletion, and emoji reactions.
  • Remote media attachments and remote custom emoji caching for inbound S2S posts.
  • Settings UI for Mastodon-compatible profile fields and advanced local settings, including CSV contact-list import and asynchronous resettling.
  • Inbox and shared-inbox request handling.
  • Input, shared input, output, and local user queues.
  • Signed ActivityPub delivery for S2S output.
  • HTTP signature checking for queued inbound S2S messages.
  • Basic S2S handling for Follow, Accept Follow, Undo Follow, Create, Update, Delete, Like, Announce, EmojiReact, Ping/Pong, and Move behavior.
  • Filesystem indexes for timeline objects, replies, likes, announces, emoji reactions, public objects, collected inboxes, blocked instances, failed instances, and public hashtags.
  • snac-style handling for content rejection filters, blocked hashtags, followed hashtag distribution, dropped DMs from unknown actors, and blocked/muted actors.
  • The Mastodon client API subset implemented by snac2, including its OAuth authorization flow.
  • A Debian 12 multi-stage container image and a single-replica Docker Swarm deployment with separate data and configuration mounts.
  • Smoke tests that exercise the implemented HTTP and storage behavior and keep selected responses aligned with snac2.

What It Is Not Yet

  • It is not a complete ActivityPub server.
  • It is not yet a conservative production recommendation for unattended public instances.
  • It does not implement Mastodon's entire API, only the subset implemented by snac2.

Development installation is documented in INSTALL.md. Container and Docker Swarm deployment is documented in DOCKER.md. Container images use the loweel/xenomorph Docker Hub repository as their publication target.

Design Shape

Xenomorph is meant to stay small.

The intended deployment model is a tiny ActivityPub instance for one person or a few users, especially in a homelab. The concrete target environment is Docker on small machines, such as ODROID-class nodes, with storage that can live on NFS. Ada plus Docker should make the executable and runtime environment highly reproducible, while filesystem storage keeps data visible and easy to reason about.

Relationship With snac2

snac2 by grunfink is the reference, inspiration, and credit source for this project. Xenomorph initially follows it as a port for the externally visible interfaces and for many internal storage and queue decisions.

The goal is not to redesign the product while porting it. The goal is to understand what snac2 does, reproduce the behavior in Ada, and then only add new direction once there is a solid foundation.

One intentional divergence is the browser admin login flow. snac2 exposes the admin page through an HTTP Basic authentication challenge; Xenomorph redirects unauthenticated /user/admin browser requests to its OAuth-style Web UI login page and then uses the xenomorph_ui session cookie for the admin shell and Web UI actions. The ActivityPub and Mastodon-compatible API compatibility tests should not assume snac2-style Basic-auth behavior for Xenomorph's Web UI.

License

Xenomorph is intended to be licensed under the European Union Public Licence, using the current version or later: EUPL-1.2-or-later.

Acknowledgements

  • snac2 and grunfink, for the design path and the practical approach this project follows.
  • Braille Institute, for the Atkinson Hyperlegible font family used by the Web UI.
  • OpenAI Codex, for refreshing my memory about Ada. The last time I seriously programmed in Ada, it was still called Ada 95 and ran on SGI machines.
  • OpenMoji, Twemoji, and Noto Emoji, for the bundled custom-emoji packs (see static/xenomorph/emoji-packs/README.md for per-pack licenses and attribution).

Contact

This is a personal project, and feedback is welcome through the channels below.

  • Email: uriel.fanelli@keinpfusch.net
  • Matrix: @uriel:chat.keinpfusch.net