Skip to content

Repository files navigation

nondogmatic, hackable, pragmatic activitypub server

Quality Gate Status

This project tries to provide a hackable, open to as much federation as possible single user or small user group activitypub server.

Get started with the documentation to get an overview.

See ARCHITECTURE.md for the ActivityPub core / client API boundary and composition rules.

Development

Prerequisites

Install Go and the local development hooks:

brew install pre-commit trufflehog
pre-commit install

The pre-commit hook runs TruffleHog to catch verified secrets before they are committed.

Running locally

To run it locally, clone the repository, install the Go dependencies, and copy the example config:

cp config.example.yml config.yml

For local development, keep the SQLite URI in config.yml pointed at a persistent file before running migrations or creating users. In-memory databases are useful for tests, but will not persist across separate CLI/server processes.

sqlite:
  uri: "file:./local.db?_pragma=foreign_keys(1)"

To init or migrate the database, you can use the db cli tool:

go run cmd/cli/migrate/main.go db  --config ./config.yml init
go run cmd/cli/migrate/main.go db  --config ./config.yml migrate

You can create user by using the admin cli tool:

go run cmd/cli/admin/main.go register --email test@example.com --username testuser --password 'Str0ngP@ssword!' --config ./config.yml

To run the server, run:

go run cmd/web/server.go ./config.yml

The repository also includes a first-party React/Vite client in frontend/. It uses the optional local client API surface (client_api.enabled: true), which exposes OAuth and Mastodon-compatible /api routes for first-party and third-party clients. For local UI development, configure frontend/.env.local from frontend/.env.example and run:

cd frontend
bun install
bun run dev

Before opening a pull request, run the same core checks as CI:

gofmt -w .
go vet ./...
go test ./...

Status

  • webfinger
  • nodeinfo
  • host-meta
  • actor profile
    • GET /users/:username
    • ActivityPub JSON response
    • public key in actor document
    • compatibility-tested against GoToSocial
    • compatibility-tested against Mastodon/Akkoma
  • inbox
    • POST /users/:username/inbox
    • signed request requirement in server mode
    • stores inbound activities
    • handles Follow
    • handles Undo Follow
    • handles Create for Notes
    • handles Delete / Update for stored Notes
    • handles Accept / Reject for outbound follows
    • handles Announce / Like notifications for local posts
  • outbox
    • GET /users/:username/outbox
    • stores local activities
    • persists local Notes
    • delivers to accepted followers
    • DB-backed pagination
    • sanitizes note content
    • generated stable ULID-based activity/object IDs
    • persistent delivery queue
    • auth/user-facing posting API via Mastodon-compatible endpoints
  • followers/following
    • followers collection
    • inbound follow acceptance
    • outbound follow flow via Mastodon-compatible endpoints
    • following collection with accepted follows

Implemented ActivityPub endpoints:

  • GET /@:username redirects to the canonical actor URL.
  • GET /users/:username returns an ActivityPub actor.
  • POST /users/:username/inbox accepts signed inbound activities and currently handles Follow, Undo Follow, Create, Delete, Update, Accept, Reject, Like, Undo Like, Announce, Undo Announce, Block, Flag, and conservative Move handling. Update with a Tombstone object removes the cached note after actor ownership validation.
  • POST /inbox provides a shared inbox for addressed local recipients.
  • GET /users/:username/outbox returns stored outbox activities.
  • GET /users/:username/followers returns accepted followers.
  • GET /users/:username/following returns accepted outbound follows.
  • GET /users/:username/collections/featured returns the actor's featured collection.
  • GET /users/:username/objects/:id returns dereferenceable local ActivityPub objects. Public/unlisted objects are public; followers-only objects require a valid signed GET from an accepted follower. Direct-message object dereferencing is intentionally disabled for now.
  • GET /users/:username/activities/:id returns dereferenceable public/unlisted local ActivityPub activities.

ActivityPub C2S mutation routes are intentionally not exposed. Local posting, following, profile updates, and media management use authenticated Mastodon-compatible API endpoints.

Federation

The server now has the basic pieces for federation and Mastodon-compatible clients: actor discovery, signed inbox requirement, signed GET support for followers-only object dereferencing, follow acceptance, durable signed delivery jobs, fetch jobs, stored local/remote notes, reply threads, polls/ActivityPub Questions, hashtag and remote custom emoji metadata, OAuth/PKCE login, account search with explicit remote resolution, follow/unfollow, timelines, account profiles, profile updates, status create/detail/edit/delete with persisted edit history, favourites, bookmarks, boosts, conversations, notifications, media uploads/serving, and followers/following collections.

Compatibility notes:

Implementation Discovery / profile resolution Follow flow Notes / statuses Threads / replies Notes
GoToSocial Covered by the Dockerized integration suite.
Mastodon Real-server smoke tested; broader compatibility still needs coverage.
BookWyrm Confirmed basic ActivityPub interop/profile and object discovery; deeper workflows need tests.
Pixelfed Confirmed profile resolution, follow acceptance, and public profile status hydration through a Pixelfed-compatible outbox resolver for non-pageable AP outboxes.
Lemmy Confirmed community/profile resolution, community outbox hydration, community boosts/reblogs, and Lemmy-compatible comment discovery behind a port.
Akkoma/Pleroma Not yet compatibility-tested.

See integration/README.md for the Dockerized GoToSocial integration suite. compat/README.md contains the older/manual local compatibility setup and checklist. The non-GoToSocial rows are production smoke-test results and should be treated as confirmed partial interoperability, not exhaustive certification.

Browser UI hosting and CORS

Mastodon-compatible browser UIs work best when served from the same origin as Gargoyle through a reverse proxy. If the UI is hosted on a separate origin, configure an explicit CORS allowlist:

web:
  cors:
    allowed_origins:
      - http://localhost:5173
    allowed_methods: [GET, POST, PUT, PATCH, DELETE, OPTIONS]
    allowed_headers: [Authorization, Content-Type]
    allow_credentials: false

Wildcard CORS origins are rejected; only trusted UI origins should be listed.

For local Fediverse compatibility setups that resolve peers such as gts.test to loopback/private addresses, opt in with exact per-host exceptions:

activitypub:
  remote_url_exceptions:
    - host: gts.test
      allow_http: true
      allow_private_ip: true

Do not allow private remote fetching for untrusted production hosts.

Implemented Mastodon-compatible client endpoints include:

  • OAuth app registration, authorization-code PKCE, token issuing, and account verification.
  • Instance metadata, account search, profile lookup/update, account statuses, relationships, follow/unfollow, followers, and following.
  • Status create/detail/edit/delete/source/history/context, including persisted edit history, favourites, bookmarks, pins, boosts, replies, polls, hashtag/custom emoji metadata, and visibility handling.
  • Media upload, metadata update/delete, attachment lookup, and public media serving.
  • Notifications list/clear/dismiss/delete.
  • Conversations list/read/delete.
  • Favourites/bookmarks lists, preferences, custom emoji compatibility responses, trends/lists/filters compatibility responses, and home/public timelines with local/remote filters.
  • A first-party React/Vite frontend that uses this Mastodon-compatible API for login, timelines, compose/reply/edit/delete, media, search, follows, notifications, conversations, and profile management.

The GoToSocial integration suite can be run with:

cd integration/gts
docker compose up -d --build
GARGOYLE_RUN_INTEGRATION=1 go test -v -count=1 .
docker compose down -v --remove-orphans

Delivery/fetch jobs can be inspected with:

go run cmd/cli/admin/main.go jobs --config ./config.yml --type delivery --status failed
go run cmd/cli/admin/main.go jobs --config ./config.yml --type fetch --status pending

A background worker automatically removes broken media metadata and old unattached uploads according to media.cleanup_interval and media.unattached_ttl. The same cleanup can be run manually with:

go run cmd/cli/admin/main.go media-cleanup --config ./config.yml --older-than 24h

Known gaps before claiming broad compatibility:

  • Akkoma/Pleroma compatibility still needs real-world testing.
  • BookWyrm, Pixelfed, Lemmy, and Mastodon interop is based on focused production smoke tests; add repeatable integration/manual compatibility checks before treating them as fully supported matrices.
  • GoToSocial integration coverage includes discovery, follow/unfollow, outbound follow, multiple visibility statuses, direct mentions, status edits/federated Update, favourites, boosts, replies, deletes, polls/votes, OAuth/token setup, delivery retry, private-host hardening, profile update federation, and media upload/fetchability, but broader real-server validation is still needed.
  • Fetch and delivery queues have basic observability and duplicate fetch suppression, but need richer operational tooling.
  • Some security limitations remain documented in LIMITATIONS.md.

For more, see https://github.com/BasixKOR/awesome-activitypub

About

A hackable, easy-to-understand, federation-friendly ActivityPub server designed for individuals or small groups.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages