{"id":13765672,"url":"https://github.com/tOkeshu/activitypub-example","last_synced_at":"2025-05-10T21:31:25.287Z","repository":{"id":51206235,"uuid":"98695778","full_name":"tOkeshu/activitypub-example","owner":"tOkeshu","description":"An ActivityPub server implementation example","archived":false,"fork":false,"pushed_at":"2021-05-24T06:45:40.000Z","size":36,"stargazers_count":127,"open_issues_count":2,"forks_count":12,"subscribers_count":9,"default_branch":"master","last_synced_at":"2024-04-14T13:39:21.072Z","etag":null,"topics":["activitypub"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"agpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tOkeshu.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-07-28T23:38:19.000Z","updated_at":"2024-03-18T20:03:11.000Z","dependencies_parsed_at":"2022-09-03T16:01:14.479Z","dependency_job_id":null,"html_url":"https://github.com/tOkeshu/activitypub-example","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tOkeshu%2Factivitypub-example","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tOkeshu%2Factivitypub-example/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tOkeshu%2Factivitypub-example/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tOkeshu%2Factivitypub-example/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tOkeshu","download_url":"https://codeload.github.com/tOkeshu/activitypub-example/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":224556977,"owners_count":17331088,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["activitypub"],"created_at":"2024-08-03T16:00:43.842Z","updated_at":"2024-11-17T01:31:15.221Z","avatar_url":"https://github.com/tOkeshu.png","language":"Python","funding_links":[],"categories":["Implementations"],"sub_categories":[],"readme":"ActivityPub example server\n==========================\n\nThis is an example server implementing a few basic features of [activitypub](https://www.w3.org/TR/activitypub/).\n\nBecause ActivityPub is quite generic, the goal here is not to implement every aspect of ActivityPub but just a coherent subset that can be used for understanding the protocol as well as for testing. Thus the use case is microblogging: users can only publish or like notes and follow each others.\n\nFeatures\n--------\n\nOutbox:\n\n- [X] Accept Create activities\n- [X] Accept Follow activities\n- [ ] Accept Like activities\n- [X] Accept non-activity objects and convert them to a Create\n  activity (only accepts Note objects for now since it is an example\n  server)\n\nDelivery:\n\n- [X] Handle `to` audience\n- [X] Handle `cc` audience\n- [X] Handle `bto` and `bcc`\n- [X] Handle `audience` audience\n\nInbox:\n\n- [X] Accept Create activities\n- [X] Accept Follow activities\n- [ ] Accept Like activities\n\nGetting started\n---------------\n\nInstall requirements (probably in a virtualenv):\n\n    $ pip install django requests\n\nClone this repository:\n\n    $ git clone https://github.com/tOkeshu/activitypub-example.git\n\nRun the migrations\n\n    $ cd activitypub\n    $ ./manage.py migrate\n\nRun the server\n\n    $ ./manage.py runserver\n\nTesting the federation\n----------------------\n\nTesting the federation is a tat trickier.\nYou can use a reverse proxy to simulate remote servers.\n\nFirst add two new hosts in your hosts file:\n\n    $ cat /etc/hosts\n    ...\n    127.0.1.1\talice.local bob.local\n    ...\n\nThen add two new virtual hosts for alice.local and bob.local.\nHere is an example nginx configuration file to achieve that:\n\n    server {\n        listen 80;\n        index index.html index.htm;\n\n        server_name alice.local;\n        server_name_in_redirect off;\n\n        location / {\n            proxy_pass http://127.0.0.1:8000;\n            proxy_set_header X-Forwarded-Host $host;\n        }\n    }\n\nCopy the file and change `alice.local` to `bob.local` and the port to `8001`.\nYou'll also need two different Django configuration files. So copy `activitypub/settings.py` to `activitypub/settings-bob.py` and change the following values:\n\n    $ cat activitypub/settings-bob.py\n    ...\n    ACTIVITYPUB_DOMAIN = \"alice.local\" # or bob.local\n    ...\n    DATABASES = {\n        'default': {\n            'ENGINE': 'django.db.backends.sqlite3',\n            'NAME': os.path.join(BASE_DIR, 'alice.sqlite3'), # or bob.sqlite3\n        }\n    }\n\n\nNow you can run the migrations for alice (default) and bob:\n\n    $ ./manage.py migrate --settings=activitypub.settings\n    $ ./manage.py migrate --settings=activitypub.settings-bob\n\nFinally run the two servers in different terminals:\n\n    $ ./manage.py runserver # alice\n    $ ./manage.py runserver 8001 --setttings=activitypub.settings-bob\n\nCheck that the servers are reachable via the correct hosts:\n\n - [http://alice.local](http://alice.local)\n - [http://bob.local](http://bob.local)\n\nIf all works correctly they should be able to reach each others. To verify that, we need to first create users. The simplest way is to do it via the python shell:\n\n    $ ./manage.py shell\n    ...\n    \u003e\u003e\u003e from activitypub.models import Person\n    \u003e\u003e\u003e alice = Person(username=\"alice\", name=\"Alice in Wonderland\")\n    \u003e\u003e\u003e alice.save()\n\nAlice's actor representation should be available at http://alice.local/@alice.\nNow do that same for Bob:\n\n    $ ./manage.py shell --settings=activitypub.settings-bob\n    ...\n    \u003e\u003e\u003e from activitypub.models import Person\n    \u003e\u003e\u003e bob = Person(username=\"bob\", name=\"Robert Paulson\")\n    \u003e\u003e\u003e bob.save()\n\nAgain, Bob's representation should be available at http://bob.local/@bob.\nLet's make Alice follow bob:\n\n    $ curl -X POST 'http://alice.local/@alice/outbox' -H \"Content-Type: application/activity+json\" -d '{\"type\": \"Follow\", \"object\": \"http://bob.local/@bob\"}'\n\nIf everything went fine, we should be able to find Bob in Alice's following collection and Alice in Bob's followers collection:\n\n- [http://alice.local/@alice/following](http://alice.local/@alice/following)\n- [http://bob.local/@bob/followers](http://bob.local/@bob/followers)\n\nIf Bob publishes a note as follow:\n\n    $ curl -X POST 'http://bob.local/@bob/outbox' -H \"Content-Type: application/activity+json\" -d '{\"type\": \"Note\", \"content\": \"Good morning!\"}'\n\nWe should be able to find the new note on Alice's instance: http://alice.local/@bob@bob.local/notes\n\nAPI\n---\n\nCreate a new note:\n\n    POST /@alice/outbox HTTP/1.1\n    Host: social.example.com\n    Content-Type: application/activity+json\n\n    {\n      \"type\": \"Create\",\n      \"to\": \"https://social.example.com/@alice/followers\",\n      \"object\": {\n        \"type\": \"Note\",\n        \"content\": \"Hello world!\"\n      }\n    }\n\nCreate a new note without an activity:\n\n    POST /@alice/outbox HTTP/1.1\n    Host: social.example.com\n    Content-Type: application/activity+json\n\n    {\n      \"type\": \"Note\",\n      \"content\": \"Hello world!\"\n    }\n\nFollow someone:\n\n    POST /@alice/outbox HTTP/1.1\n    Host: social.example.com\n    Content-Type: application/activity+json\n\n    {\n      \"type\": \"Follow\",\n      \"object\": \"https://social.example.com/@bob\"\n    }\n\nLicense\n-------\n\nThis ActivityPub example server is released under the terms of the\n[GNU Affero General Public License v3](http://www.gnu.org/licenses/agpl-3.0.html)\nor later.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FtOkeshu%2Factivitypub-example","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FtOkeshu%2Factivitypub-example","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FtOkeshu%2Factivitypub-example/lists"}