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.
Install Go and the local development hooks:
brew install pre-commit trufflehog
pre-commit installThe pre-commit hook runs TruffleHog to catch verified secrets before they are committed.
To run it locally, clone the repository, install the Go dependencies, and copy the example config:
cp config.example.yml config.ymlFor 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 migrateYou 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.ymlTo run the server, run:
go run cmd/web/server.go ./config.ymlThe 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 devBefore opening a pull request, run the same core checks as CI:
gofmt -w .
go vet ./...
go test ./...- 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
CreateforNotes - handles
Delete/Updatefor storedNotes - handles
Accept/Rejectfor outbound follows - handles
Announce/Likenotifications 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 /@:usernameredirects to the canonical actor URL.GET /users/:usernamereturns an ActivityPub actor.POST /users/:username/inboxaccepts signed inbound activities and currently handlesFollow,Undo Follow,Create,Delete,Update,Accept,Reject,Like,Undo Like,Announce,Undo Announce,Block,Flag, and conservativeMovehandling.Updatewith aTombstoneobject removes the cached note after actor ownership validation.POST /inboxprovides a shared inbox for addressed local recipients.GET /users/:username/outboxreturns stored outbox activities.GET /users/:username/followersreturns accepted followers.GET /users/:username/followingreturns accepted outbound follows.GET /users/:username/collections/featuredreturns the actor's featured collection.GET /users/:username/objects/:idreturns 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/:idreturns 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.
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.
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: falseWildcard 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: trueDo 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-orphansDelivery/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 pendingA 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 24hKnown 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