A full-featured ActivityPub server for networked publishing with Ghost
236
stars
2,151
commits
TypeScript
primary language
Sep 11, 2026
updated
A multi-tenant ActivityPub server for Ghost, built with Fedify. This service makes it possible for independent websites to publish their content directly to the Fediverse, enabling networked publishing to the open social web.
All requests to /.ghost/activitypub/*, /.well-known/webfinger and /.well-known/nodeinfo are proxied to this ActivityPub service using nginx. All other requests are forwarded to Ghost. This setup has only been tested on macOS using Docker for Mac and OrbStack.
Install from source instructionstailscale funnel 80 or ngrok http 80 to expose your local port 80 (not 2368!), on which nginx will be runningconfig.local.json file under ghost/core/config.local.json, with the following configuration:{
"url": <Tailscale or ngrok URL from step 2>
}
pnpm dev && pnpm logs in the root directory of this projectpnpm dev in the Ghost monorepoFor AI assistants: See AGENTS.md for comprehensive guidance with code examples. For developers: See Architecture Decision Records in
/adrfor detailed rationale.
This service follows Domain-Driven Design with specific patterns:
src/
├── account/ # Immutable entities
├── post/ # Being migrated to immutable
├── activity-handlers/ # Class-based handlers
├── http/api/ # REST controllers
├── core/ # Shared utilities
└── dispatchers.ts # Legacy - don't add here
1. Database lookups MUST use SHA256 hashes (ADR-0009)
where('ap_id', apId) - it returns empty results silently!whereRaw('ap_id_hash = UNHEX(SHA2(?, 256))', [apId])ap_id, domain (with LOWER), ap_inbox_url (with LOWER)2. Result types require helper functions
isError(result), getValue(result), getError(result)[error, value]3. Services must use repositories
4. Dependency injection names must match
accountService → registered as 'accountService'db → registered as 'db'5. Avoid these anti-patterns:
dispatchers.ts → create new handler classesAccountType → use Account entityFor complete code examples demonstrating correct patterns, see AGENTS.md.
We use Biome for code formatting and linting.
If you use VS Code, you can install the Biome extension to get inline feedback.
To enable auto-formatting on save, you'll need to set the default formatter to Biome and enable Format on Save in your VS Code settings.
Tests run within a Docker Compose stack. Common commands:
pnpm test — run everything (types, unit, integration, and e2e); slowpnpm test:unit — unit tests only; fastpnpm test:integration — integration tests onlypnpm test:cucumber — end-to-end (Cucumber) testspnpm test:single 'path/to/test' — run a single unit or integration testpnpm test:types — type-check with tscSee AGENTS.md for the full list and testing conventions.
Run pnpm migrate to apply pending up migrations against your dev db (this also happens automatically on pnpm dev). For the testing db, use docker compose run migrate-testing up.
To run other migrate commands, drop into a shell with docker compose exec -it migrate /bin/sh (or migrate-testing). The migrate binary is available there, along with a MYSQL_DB environment variable correctly formatted for the -database argument.
Copyright (c) 2013-2026 Ghost Foundation - Released under the MIT license. Ghost and the Ghost Logo are trademarks of Ghost Foundation Ltd. Please see our trademark policy for info on acceptable usage.
TypeScript
92.6%
JavaScript
5.0%
Gherkin
1.5%
A full-featured ActivityPub server for networked publishing with Ghost
236
stars
2,151
commits
TypeScript
primary language
Sep 11, 2026
updated
A multi-tenant ActivityPub server for Ghost, built with Fedify. This service makes it possible for independent websites to publish their content directly to the Fediverse, enabling networked publishing to the open social web.
All requests to /.ghost/activitypub/*, /.well-known/webfinger and /.well-known/nodeinfo are proxied to this ActivityPub service using nginx. All other requests are forwarded to Ghost. This setup has only been tested on macOS using Docker for Mac and OrbStack.
Install from source instructionstailscale funnel 80 or ngrok http 80 to expose your local port 80 (not 2368!), on which nginx will be runningconfig.local.json file under ghost/core/config.local.json, with the following configuration:{
"url": <Tailscale or ngrok URL from step 2>
}
pnpm dev && pnpm logs in the root directory of this projectpnpm dev in the Ghost monorepoFor AI assistants: See AGENTS.md for comprehensive guidance with code examples. For developers: See Architecture Decision Records in
/adrfor detailed rationale.
This service follows Domain-Driven Design with specific patterns:
src/
├── account/ # Immutable entities
├── post/ # Being migrated to immutable
├── activity-handlers/ # Class-based handlers
├── http/api/ # REST controllers
├── core/ # Shared utilities
└── dispatchers.ts # Legacy - don't add here
1. Database lookups MUST use SHA256 hashes (ADR-0009)
where('ap_id', apId) - it returns empty results silently!whereRaw('ap_id_hash = UNHEX(SHA2(?, 256))', [apId])ap_id, domain (with LOWER), ap_inbox_url (with LOWER)2. Result types require helper functions
isError(result), getValue(result), getError(result)[error, value]3. Services must use repositories
4. Dependency injection names must match
accountService → registered as 'accountService'db → registered as 'db'5. Avoid these anti-patterns:
dispatchers.ts → create new handler classesAccountType → use Account entityFor complete code examples demonstrating correct patterns, see AGENTS.md.
We use Biome for code formatting and linting.
If you use VS Code, you can install the Biome extension to get inline feedback.
To enable auto-formatting on save, you'll need to set the default formatter to Biome and enable Format on Save in your VS Code settings.
Tests run within a Docker Compose stack. Common commands:
pnpm test — run everything (types, unit, integration, and e2e); slowpnpm test:unit — unit tests only; fastpnpm test:integration — integration tests onlypnpm test:cucumber — end-to-end (Cucumber) testspnpm test:single 'path/to/test' — run a single unit or integration testpnpm test:types — type-check with tscSee AGENTS.md for the full list and testing conventions.
Run pnpm migrate to apply pending up migrations against your dev db (this also happens automatically on pnpm dev). For the testing db, use docker compose run migrate-testing up.
To run other migrate commands, drop into a shell with docker compose exec -it migrate /bin/sh (or migrate-testing). The migrate binary is available there, along with a MYSQL_DB environment variable correctly formatted for the -database argument.
Copyright (c) 2013-2026 Ghost Foundation - Released under the MIT license. Ghost and the Ghost Logo are trademarks of Ghost Foundation Ltd. Please see our trademark policy for info on acceptable usage.
TypeScript
92.6%
JavaScript
5.0%
Gherkin
1.5%