Mirror of Forgejo for evaluating jojo.build customizations.
Find a file
Gergely Nagy 0295658650 Federated user activity following (#4767)
This is a implementation of #4277.

The core idea is that any activity (where activity is defined as anything that ends up in the `action` table) will be wrapped in an `ap.Note`, and sent to followers. Similarly, the inbox of local users now accepts such Notes. Additionally, there's now a "Feeds" tab on the user profile page, which displays the received notes.

# Preview

![Preview](/attachments/9ce6a138-a748-447e-8e8b-d6143564ee4e)

# How to Try?

The PR can be tried using a single Forgejo instance, but two distinct ones probably shows how it works better. For the sake of simplicity, lets try with a single instance. This is how to get started:

1. Enable federation
2. Subscribe one user to another (or to themselves):
    ```
    curl -s -H "authorization: Bearer ${TOKEN}" -XPOST \
         http://localhost:3000/api/v1/user/activitypub/follow \
         --json '{"target": "http://localhost:3000/api/v1/activitypub/user-id/1"}'
    ```

    This makes the first user follow themselves.
3. Create a repo, open an issue, or basically do anything that results in an activity recorded.
4. Visit `http://localhost:3000/{username}?tab=feed` to see the feed in action.

If you want to try with multiple instances, then it's very similar: you just change the `actor_id` to the IRI of the user you want to follow the first instance's user with, and then you can look at the feed tab of this user on the second instance, after you performed some activity on the first.

## Trying with Mastodon / GoToSocial

To try with Mastodon or GoToSocial, you will likely need to bring your Forgejo instance public, and behind https. Once your Forgejo instance is up, you can search for `@yourusername@forgejo.your.domain.example.com`, and simply follow your Forgejo account. Creating any activity will then happily federate to Mastodon & GoToSocial.

You can also copy & paste the Forge user's web profile URL (eg, `https://forgejo.your.domain.example.com/yourusername`) into your fedi client of choice, and it will discover the profile that way too.

# Testing

* test: https://codeberg.org/meissa/federation/src/branch/federated-user-activity-following/doc/user-activity-following/manual-test.md
* Proof of gts->forgejo: https://social.meissa-gmbh.de/@meissa/114499541149466596
* Proof of forgejo->gts: https://social.meissa-gmbh.de/@meissa/114505225265720094

## Architecture decisions

There are a number of ways user activity federation could be implemented. One way - which I explored first - is to wrap each activity, and send those, and let the client render it. The advantage of this would be that we'd be able to have references to other objects (comments, repos, etc). The disadvantage is that doing this requires making all of these things addressable, and that's a lot of work. Another disadvantage is that this requires every client to know how to display it.

Another way, chosen here, is to send a rendered HTML `ap.Note` instead, with an `AttributedTo` (`ap.Person`) property, which describes the activity that happened in a HTML note. This is much simpler to implement, and has the huge advantage that it is also easier to display. In fact, once we have http signatures, we should be able to federate user activity to Mastodon, too! (Though this also requires figuring out how Mastodon wants to follow a user...)

Since user activity federation is mostly cosmetic, as in, it's there for the user to see, rather than for programs to take actions based upon this activity, I believe that sending an `ap.Note` is preferable over a more machine-oriented approach.

## Limitations & TODO

### FederatedUser

We should be caching the Avatar in a similar way. For that, though, we also need to store the last activity of a federated user, so we can expire old avatars from the cache. The avatar refresh part will be covered by #4778.

### Notes

While sending out notes, the `AttributedTo` property is set to an `ap.Person`, based on the originating local user. This is currently unused. The idea is that once following is implemented properly (see above), we'll be able to link this  to a FederatedUser (and thus to ExternalUser & User), which will allow us to display avatars and such, too.

### Display

The template used for displaying the received activities is currently incredibly simplistic. That's probably ok, it doesn't need to be fantastic.

### TODO

- [x] Fix the crashes on certain ops:
  - [x] Issue/PR close & reopen
- [x] Figure out a better way to implement follows
- [x] Store the `AttributedTo` part of the note, too, the ID of it.
- [x] Make sure only those activities are sent out that need to be.
      Currently, pretty much any activity is sent out, even private ones. We should be a bit smarter about that.
- [x] Make the ids used in the AP messages deterministic
      The IDs used in the AP messages are currently UUIDs, and we do not store them, so all the IRIs are "invalid": the objects they refer to don't exist outside of the AP message itself. We should be able to reconstruct the Note objects and Create activities from their IDs.
- [x] Make it possible to follow Forgejo account from Mastodon and GtS
  - [x] Mastodon without `AUTHORIZED_FETCH` works
  - [x] GoToSocial can follow
  - [x] Mastodon with `AUTHORIZED_FETCH` can follow
- ~~Create a cron job to refresh federated user avatars~~
- [x] Implement unfollowing
- [x] Add a `<link rel="alternate" type="application/activity+json" href="...">` to profile pages
      This lets Mastodon & most other Fedi frontends discover the AP profile just by pasting a Forgejo user's web profile page into a search box, without having to know the corresponding AP actor URL
- [x] Make it easier to make a local user follow a remote AP actor
- ~~Rebase on top of #4778 by @realaravinth, once that is ready~~
- [x] Create an API endpoint to list the AP feed
- [x] Create a DB migration for the new stuff
- [x] Make swagger stuff happy
- [x] Clean up the commit history
- [x] ~~Tests~~ Opting for manual testing for now.

Co-authored-by: Michael Jerger <michael.jerger@meissa-gmbh.de>
Co-authored-by: famfo <famfo@famfo.xyz>
Co-authored-by: jerger <jerger@noreply.codeberg.org>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/4767
Reviewed-by: jerger <jerger@noreply.codeberg.org>
Reviewed-by: elle <0xllx0@noreply.codeberg.org>
2026-05-08 08:08:10 +02:00
.devcontainer chore: rename devcontainer name (#12356) 2026-05-01 00:20:15 +02:00
.forgejo Update https://data.forgejo.org/actions/setup-forgejo action to v3.1.11 (forgejo) (#12429) 2026-05-06 05:22:06 +02:00
.semgrep chore: flag suspicious OwnerID comparisons (#12184) 2026-04-19 04:24:09 +02:00
assets Update module github.com/redis/go-redis/v9 to v9.19.0 (forgejo) (#12309) 2026-05-04 05:15:07 +02:00
build feat(build): Support go "fmt" format strings as masked usage patterns (#12013) 2026-05-01 02:46:01 +02:00
cmd feat: add name & description columns to authorized integration DB table (#12413) 2026-05-05 02:58:47 +02:00
contrib chore: rename 'forgejo_migrations' to 'forgejo_migrations_legacy' 2025-10-14 14:40:49 -06:00
custom/conf feat: cache OIDC metadata & JWKS when read by authorized integration (#12275) 2026-04-28 02:13:06 +02:00
docker chore(Dockerfile.rootless): update shadowed env variables (#11720) 2026-04-16 09:58:57 +02:00
models fix: Prevent unremovable review requests after submitting pending reviews (#12302) 2026-05-08 05:52:59 +02:00
modules fix: return the error when InitDelegateHooks fail (#12427) 2026-05-06 04:47:15 +02:00
options chore(i18n): move 89 strings to JSON (#12280) 2026-04-27 16:07:51 +02:00
public feat: Follow remote users; feed tab (#10380) 2026-04-12 03:31:03 +02:00
release-notes fix: Prevent unremovable review requests after submitting pending reviews (#12302) 2026-05-08 05:52:59 +02:00
release-notes-published chore(release-notes): Forgejo v15.0.1 (#12314) 2026-04-29 14:37:45 +02:00
releases/images [DOCS] RELEASE-NOTES.md 2024-02-05 14:44:32 +01:00
routers Federated user activity following (#4767) 2026-05-08 08:08:10 +02:00
services feat: expose AGit topic branch in API PR head label (#12352) 2026-05-08 04:46:57 +02:00
templates chore: dialog modal max-width rendering failure (#12469) 2026-05-08 08:01:34 +02:00
tests feat: expose AGit topic branch in API PR head label (#12352) 2026-05-08 04:46:57 +02:00
tools chore: move backend-checks CI checks to Makefile: make pr-go (#11053) 2026-02-17 02:41:40 +01:00
web_src Lock file maintenance (forgejo) (#12408) 2026-05-06 14:55:06 +02:00
.air.toml chore: rename 'migrations' to 'gitea_migrations' 2025-10-14 14:40:49 -06:00
.deadcode-out feat: cache OIDC metadata & JWKS when read by authorized integration (#12275) 2026-04-28 02:13:06 +02:00
.dockerignore fix: Dockerfile should re-use bindata files when possible 2025-06-13 14:00:57 +02:00
.editorconfig i18n(next): convert indention style to tabs: en, editorconfig (#10661) 2026-01-02 05:56:48 +01:00
.envrc.example Make direnv optional to let developers use their own direnv configuration 2024-11-06 20:34:49 +01:00
.gitattributes Add interface{} to any replacement to make fmt, exclude *.pb.go (#30461) 2024-04-15 20:01:36 +02:00
.gitignore chore: polish linter error vs. dead code reporting (#11217) 2026-03-20 07:06:09 +01:00
.gitmodules cleanup(tests): remove manual testing submodule 2024-04-21 10:13:51 +02:00
.gitpod.yml Remove sqlite-viewer and using database client (#31223) 2024-06-09 11:13:39 +02:00
.golangci.yml refactor: change authentication to return structured data (#12202) 2026-04-22 21:00:26 +02:00
.ignore Add /options/license and /options/gitignore to .ignore (#30219) 2024-04-07 15:40:31 +02:00
.mailmap Add .mailmap with aliases for Unknwon (github.com/Unknwon) 2024-08-14 08:26:16 -04:00
.markdownlint.yaml Update JS dependencies (#28537) 2023-12-30 05:29:03 +00:00
.mockery.yml feat: allow Forgejo Actions to be used an Authorized Integration in-memory with internal issuer (#12364) 2026-05-01 17:42:34 +02:00
.node-version Update Node.js to v24.15.0 (forgejo) (#12157) 2026-04-17 06:59:26 +02:00
.npmrc Upgrade to npm lockfile v3 and explicitely set it (#23561) 2023-03-18 19:38:10 +01:00
.release-notes-assistant.yaml chore: fix rna config (#12304) 2026-04-28 18:56:52 +02:00
.spectral.yaml Add spectral linter for Swagger (#20321) 2022-07-11 18:07:16 -05:00
.yamllint.yaml fully replace drone with actions (#27556) 2023-10-11 06:39:32 +00:00
BSDmakefile feat: Makefile & BSDmakefile changes (#7455) 2025-04-27 10:04:32 +00:00
CODEOWNERS chore: add @0xllx0 to federation codeowners (#10716) 2026-01-09 23:53:06 +01:00
CONTRIBUTING.md docs: replace Developer Guide link with the new Contributor Guide one. 2024-08-26 13:22:39 +03:00
DCO Remove address from DCO (#22595) 2023-01-24 18:52:38 +00:00
Dockerfile Update data.forgejo.org/oci/golang Docker tag to v1.26 (forgejo) (#11662) 2026-03-13 08:17:21 +01:00
Dockerfile.rootless Update data.forgejo.org/oci/golang Docker tag to v1.26 (forgejo) (#11662) 2026-03-13 08:17:21 +01:00
eslint.config.mjs Update linters (forgejo) (#11627) 2026-03-12 18:27:43 +01:00
flake.lock chore: bump nixpkgs in flake.lock (#10128) 2025-11-16 01:18:26 +01:00
flake.nix refactor: Simplify flake.nix (#9805) 2025-10-22 19:09:11 +02:00
go.mod Update go toolchain directive to v1.26.3 (forgejo) (#12454) 2026-05-07 20:04:54 +02:00
go.sum chore: upgrade xorm to v1.3.9-forgejo.12 (#12430) 2026-05-06 12:02:58 +02:00
LICENSE Forgejo v9.0 is GPLv3+ 2024-08-22 09:09:29 +02:00
main.go fix: do not mix urfave v2 with urfave v3 (#8168) 2025-06-12 15:38:03 +02:00
Makefile Update renovate Docker tag to v43.160.6 (forgejo) (#12404) 2026-05-04 02:05:26 +02:00
manifest.scm Add a GNU Guix manifest (#8038) 2025-06-03 08:08:17 +02:00
package-lock.json Lock file maintenance (forgejo) (#12408) 2026-05-06 14:55:06 +02:00
package.json Update dependency globals to v17.6.0 (forgejo) (#12417) 2026-05-05 09:43:48 +02:00
playwright.config.ts chore: remove webkit and mobile safari from playwright (#10103) 2025-11-13 17:23:08 +01:00
README.md chore: fix a few typos in the documentation (#9134) 2025-09-04 01:53:40 +02:00
release-notes-assistant.sh fix(rna): prioritize breaking changes without a feature or bug label over non-breaking changes (#12124) 2026-04-17 15:24:58 +02:00
RELEASE-NOTES.md chore(release-notes): fix release notes of chroma update in v8.0.0 2025-10-05 17:10:38 +05:00
shell.nix chore: use interactive sqlite via nix (#10439) 2025-12-17 13:20:33 +01:00
stylelint.config.js Merge pull request 'Port "Enable declaration-block-no-redundant-longhand-properties (#30950)' (#3769) from beowulf/gitea-port-pull-30950 into forgejo 2024-05-14 22:23:54 +00:00
tailwind.config.js chore(ui): change /devtest to /-/demo (#11019) 2026-01-26 13:12:25 +01:00
tsconfig.json feat(ui): replace Monaco with CodeMirror (#10559) 2026-01-04 23:52:33 +01:00
vitest.config.ts feat(ui): replace Monaco with CodeMirror (#10559) 2026-01-04 23:52:33 +01:00
webpack.config.js chore(ui): change /devtest to /-/demo (#11019) 2026-01-26 13:12:25 +01:00

Welcome to Forgejo

Hi there! Tired of big platforms playing monopoly? Providing Git hosting for your project, friends, company or community? Forgejo (/for'd͡ʒe.jo/ inspired by forĝejo the Esperanto word for forge) has you covered with its intuitive interface, light and easy hosting and a lot of built-in functionality.

Forgejo was created in 2022 because we think that the project should be owned by an independent community. If you second that, then Forgejo is for you! Our promise: Independent Free/Libre Software forever!

What does Forgejo offer?

If you like any of the following, Forgejo is literally meant for you:

  • Lightweight: Forgejo can easily be hosted on nearly every machine. Running on a Raspberry? Small cloud instance? No problem!
  • Project management: Besides Git hosting, Forgejo offers issues, pull requests, wikis, kanban boards and much more to coordinate with your team.
  • Publishing: Have something to share? Use releases to host your software for download, or use the package registry to publish it for docker, npm and many other package managers.
  • Customizable: Want to change your look? Change some settings? There are many config switches to make Forgejo work exactly like you want.
  • Powerful: Organizations & team permissions, CI integration, Code Search, LDAP, OAuth and much more. If you have advanced needs, Forgejo has you covered.
  • Privacy: From update checker to default settings: Forgejo is built to be privacy first for you and your crew.
  • Federation: (WIP) We are actively working to connect software forges with each other through ActivityPub, and create a collaborative network of personal instances.

Learn more

Dive into the documentation, subscribe to releases and blog post on our website, find us on the Fediverse or hop into our Matrix room if you have any questions or want to get involved.

License

Forgejo is distributed under the terms of the GPL version 3.0 or any later version.

The agreement for this license was documented in June 2023 and implemented during the development of Forgejo v9.0. All Forgejo versions before v9.0 are distributed under the MIT license.

Get involved

If you are interested in making Forgejo better, either by reporting a bug or by changing the governance, please take a look at the contribution guide.