Плагин ActivityPub

@hellekin Спасибо за отчёт. На сервисе ActivityPub всегда будет происходить определённое количество неудачных запросов, так как акторы в федериве появляются и исчезают. Например, похоже на то, что:

Подробность логов предназначена для отладки, однако если они засоряют ваши логи, вы можете отключить их в настройках сайта activity pub verbose logging. По умолчанию эта опция выключена.

Мы планируем внести улучшения в обработку ошибок во второй фазе, если это потребуется, но пока что опубликованные вами фрагменты выглядят ожидаемыми, то есть акторы действительно больше не находятся в федериве.

В настоящее время плагин обрабатывает сбои доставки так же, как и Mastodon: если в течение 7 дней запросы к конечной точке не удаются, она помечается как «недоступная», и попытки выполнения запросов к ней прекращаются.

3 лайка

Да, как указано в строке перед этим.

Действительно, но код состояния — 410, что означает, что учётная запись могла быть перемещена (если есть «гробница» — проверяется ли это условие?)

1 лайк

Нет. Это ошибка 410 Gone, означающая, что ресурс удалён. Чего я сейчас не понимаю?

1 лайк

Вы упустили это:

Привет, @angus,

Я только что настроил новый саморазмещённый Discourse с вашим плагином Activity Pub по адресу https://federation.cafe и вижу некоторые ошибки 403 в логах ошибок Discourse (и посты не публикуются).

Интересует, может ли это быть связано с наличием дефисов?

[Discourse Activity Pub] GET-запрос к https://bofh.social/internal/fetch не удался: Ожидалось([200, 201, 202, 301, 302, 307, 308]) <= Фактически(403 Forbidden)

/var/www/discourse/plugins/discourse-activity-pub/lib/discourse_activity_pub/request.rb:66:in `rescue in perform'
/var/www/discourse/plugins/discourse-activity-pub/lib/discourse_activity_pub/request.rb:50:in `perform'
/var/www/discourse/plugins/discourse-activity-pub/lib/discourse_activity_pub/request.rb:34:in `get_json_ld'
/var/www/discourse/plugins/discourse-activity-pub/lib/discourse_activity_pub/request.rb:106:in `get_json_ld'
/var/www/discourse/plugins/discourse-activity-pub/lib/discourse_activity_pub/json_ld.rb:52:in `request_object'
/var/www/discourse/plugins/discourse-activity-pub/lib/discourse_activity_pub/json_ld.rb:48:in `resolve_object'
/var/www/discourse/plugins/discourse-activity-pub/lib/discourse_activity_pub/ap/actor.rb:103:in `resolve_and_store'
/var/www/discourse/plugins/discourse-activity-pub/app/controllers/concerns/discourse_activity_pub/signature_verification.rb:192:in `actor_from_key_id'
/var/www/discourse/plugins/discourse-activity-pub/app/controllers/concerns/discourse_activity_pub/signature_verification.rb:57:in `signed_request_actor'
/var/www/discourse/plugins/discourse-activity-pub/app/controllers/concerns/discourse_activity_pub/signature_verification.rb:27:in `ensure_verified_signature'
activesupport-7.0.4.3/lib/active_support/callbacks.rb:400:in `block in make_lambda'
activesupport-7.0.4.3/lib/active_support/callbacks.rb:180:in `block (2 levels) in halting_and_conditional'
actionpack-7.0.4.3/lib/abstract_controller/callbacks.rb:34:in `block (2 levels) in <module:Callbacks>'
activesupport-7.0.4.3/lib/active_support/callbacks.rb:181:in `block in halting_and_conditional'
activesupport-7.0.4.3/lib/active_support/callbacks.rb:595:in `block in invoke_before'
activesupport-7.0.4.3/lib/active_support/callbacks.rb:595:in `each'
activesupport-7.0.4.3/lib/active_support/callbacks.rb:595:in `invoke_before'
activesupport-7.0.4.3/lib/active_support/callbacks.rb:116:in `block in run_callbacks'
/var/www/discourse/app/controllers/application_controller.rb:418:in `block in with_resolved_locale'
i18n-1.14.1/lib/i18n.rb:322:in `with_locale'
/var/www/discourse/app/controllers/application_controller.rb:418:in `with_resolved_locale'
activesupport-7.0.4.3/lib/active_support/callbacks.rb:127:in `block in run_callbacks'
activesupport-7.0.4.3/lib/active_support/callbacks.rb:138:in `run_callbacks'
actionpack-7.0.4.3/lib/abstract_controller/callbacks.rb:233:in `process_action'
actionpack-7.0.4.3/lib/action_controller/metal/rescue.rb:22:in `process_action'
actionpack-7.0.4.3/lib/action_controller/metal/instrumentation.rb:67:in `block in process_action'
activesupport-7.0.4.3/lib/active_support/notifications.rb:206:in `block in instrument'
activesupport-7.0.4.3/lib/active_support/notifications/instrumenter.rb:24:in `instrument'
activesupport-7.0.4.3/lib/active_support/notifications.rb:206:in `instrument'
actionpack-7.0.4.3/lib/action_controller/metal/instrumentation.rb:66:in `process_action'
actionpack-7.0.4.3/lib/action_controller/metal/params_wrapper.rb:259:in `process_action'
activerecord-7.0.4.3/lib/active_record/railties/controller_runtime.rb:27:in `process_action'
actionpack-7.0.4.3/lib/abstract_controller/base.rb:151:in `process'
actionview-7.0.4.3/lib/action_view/rendering.rb:39:in `process'
rack-mini-profiler-3.1.0/lib/mini_profiler/profiling_methods.rb:85:in `block in profile_method'
actionpack-7.0.4.3/lib/action_controller/metal.rb:188:in `dispatch'
actionpack-7.0.4.3/lib/action_controller/metal.rb:251:in `dispatch'
actionpack-7.0.4.3/lib/action_dispatch/routing/route_set.rb:49:in `dispatch'
actionpack-7.0.4.3/lib/action_dispatch/routing/route_set.rb:32:in `serve'
actionpack-7.0.4.3/lib/action_dispatch/journey/router.rb:50:in `block in serve'
actionpack-7.0.4.3/lib/action_dispatch/journey/router.rb:32:in `each'
actionpack-7.0.4.3/lib/action_dispatch/journey/router.rb:32:in `serve'
actionpack-7.0.4.3/lib/action_dispatch/routing/route_set.rb:852:in `call'
railties-7.0.4.3/lib/rails/engine.rb:530:in `call'
railties-7.0.4.3/lib/rails/railtie.rb:226:in `public_send'
railties-7.0.4.3/lib/rails/railtie.rb:226:in `method_missing'
actionpack-7.0.4.3/lib/action_dispatch/routing/mapper.rb:19:in `block in <class:Constraints>'
actionpack-7.0.4.3/lib/action_dispatch/routing/mapper.rb:48:in `serve'
actionpack-7.0.4.3/lib/action_dispatch/journey/router.rb:50:in `block in serve'
actionpack-7.0.4.3/lib/action_dispatch/journey/router.rb:32:in `each'
actionpack-7.0.4.3/lib/action_dispatch/journey/router.rb:32:in `serve'
actionpack-7.0.4.3/lib/action_dispatch/routing/route_set.rb:852:in `call'
/var/www/discourse/lib/middleware/omniauth_bypass_middleware.rb:74:in `call'
rack-2.2.7/lib/rack/tempfile_reaper.rb:15:in `call'
rack-2.2.7/lib/rack/conditional_get.rb:27:in `call'
rack-2.2.7/lib/rack/head.rb:12:in `call'
actionpack-7.0.4.3/lib/action_dispatch/http/permissions_policy.rb:38:in `call'
/var/www/discourse/lib/content_security_policy/middleware.rb:12:in `call'
/var/www/discourse/lib/middleware/anonymous_cache.rb:367:in `call'
rack-2.2.7/lib/rack/session/abstract/id.rb:266:in `context'
rack-2.2.7/lib/rack/session/abstract/id.rb:260:in `call'
actionpack-7.0.4.3/lib/action_dispatch/middleware/cookies.rb:704:in `call'
actionpack-7.0.4.3/lib/action_dispatch/middleware/callbacks.rb:27:in `block in call'
activesupport-7.0.4.3/lib/active_support/callbacks.rb:99:in `run_callbacks'
actionpack-7.0.4.3/lib/action_dispatch/middleware/callbacks.rb:26:in `call'
actionpack-7.0.4.3/lib/action_dispatch/middleware/debug_exceptions.rb:28:in `call'
actionpack-7.0.4.3/lib/action_dispatch/middleware/show_exceptions.rb:26:in `call'
logster-2.12.2/lib/logster/middleware/reporter.rb:43:in `call'
railties-7.0.4.3/lib/rails/rack/logger.rb:40:in `call_app'
railties-7.0.4.3/lib/rails/rack/logger.rb:27:in `call'
/var/www/discourse/config/initializers/100-quiet_logger.rb:20:in `call'
/var/www/discourse/config/initializers/100-silence_logger.rb:29:in `call'
actionpack-7.0.4.3/lib/action_dispatch/middleware/remote_ip.rb:93:in `call'
actionpack-7.0.4.3/lib/action_dispatch/middleware/request_id.rb:26:in `call'
/var/www/discourse/lib/middleware/enforce_hostname.rb:24:in `call'
rack-2.2.7/lib/rack/method_override.rb:24:in `call'
actionpack-7.0.4.3/lib/action_dispatch/middleware/executor.rb:14:in `call'
rack-2.2.7/lib/rack/sendfile.rb:110:in `call'
actionpack-7.0.4.3/lib/action_dispatch/middleware/host_authorization.rb:131:in `call'
rack-mini-profiler-3.1.0/lib/mini_profiler.rb:260:in `call'
message_bus-4.3.2/lib/message_bus/rack/middleware.rb:60:in `call'
/var/www/discourse/lib/middleware/request_tracker.rb:228:in `call'
railties-7.0.4.3/lib/rails/engine.rb:530:in `call'
railties-7.0.4.3/lib/rails/railtie.rb:226:in `public_send'
railties-7.0.4.3/lib/rails/railtie.rb:226:in `method_missing'
rack-2.2.7/lib/rack/urlmap.rb:74:in `block in call'
rack-2.2.7/lib/rack/urlmap.rb:58:in `each'
rack-2.2.7/lib/rack/urlmap.rb:58:in `call'
unicorn-6.1.0/lib/unicorn/http_server.rb:634:in `process_client'
unicorn-6.1.0/lib/unicorn/http_server.rb:739:in `worker_loop'
unicorn-6.1.0/lib/unicorn/http_server.rb:547:in `spawn_missing_workers'
unicorn-6.1.0/lib/unicorn/http_server.rb:143:in `start'
unicorn-6.1.0/bin/unicorn:128:in `<top (required)>'
/var/www/discourse/vendor/bundle/ruby/3.2.0/bin/unicorn:25:in `load'
/var/www/discourse/vendor/bundle/ruby/3.2.0/bin/unicorn:25:in `<main>'

hostname	LILEJAP07-app
process_id	658
application_version	1abfe2e61d12b1b559aab0132ec3fb7cc8b87232
HTTP_HOST	federation.cafe
REQUEST_URI	/ap/actor/5ce52c043e670476a1426f9a66472c07
REQUEST_METHOD	GET
HTTP_USER_AGENT	Akkoma 3.8.0-0-gccae7ef; https://bofh.social <admin@bofh.social>
HTTP_ACCEPT	application/activity+json
HTTP_X_FORWARDED_FOR	91.107.215.39, 172.71.250.70
HTTP_X_REAL_IP	172.71.250.70
time	14:16

Есть какие-то идеи?

Меня также интересуют полноценные посты. В таком случае следует использовать тип «статья», а не «заметка».

На самом деле, анонсы для меня гораздо менее интересны, чем функции федерированных форумов.

Есть ли дорожная карта и/или возможность отправить пожертвования на разработку таких функций?

Привет @gme, спасибо, что опробовали плагин.

Несколько моментов, которые стоит отметить изначально:

  1. Плагин MVP протестирован в связке с Mastodon. Я вижу, что вы используете Pleroma. Я знаю, что Pleroma соответствует ActivityPub и работает с Mastodon, однако мы ещё не детально изучали, какие именно доработки (если они вообще нужны) потребуются для обеспечения его поддержки. Но нам всё же интересно узнать, что происходит в вашем случае.

  2. Похоже, что запросы не прошли из-за ошибки аутентификации на вашем сервере Pleroma (именно это означает ошибка 403). Поскольку я могу выполнить GET-запрос к этому эндпоинту без аутентификации через cURL, я подозреваю, что проблема может быть в HTTP-аутентификации на стороне Pleroma.

Чтобы проверить второе (то есть пункт 2), не могли бы вы, если возможно, посмотреть логи вашего сервера Pleroma (похоже, вы там тоже администратор?), чтобы получить более подробную информацию об этой части процесса?

Спасибо за обратную связь, @bmann. Не могли бы вы подробнее рассказать о сценарии использования, который вы имеете в виду? Желательно с примером.

Эта тема — лучшее место, чтобы быть в курсе развития событий. Когда мы определимся с планом на второй этап, я опубликую его здесь. Тем временем лучший способ помочь — это делиться конкретными сценариями использования, которые вы применяете или хотели бы применять для этого плагина.

5 лайков

Сценарий использования заключается в применении Discourse как полноценного узла ActivityPub. Существует множество более простых способов публикации контента в ActivityPub (например, использование RSS-лент категорий и сервисов Zapier или Buffer), однако реализация расширенных возможностей ActivityPub возможна только в виде плагина или интеграции.

Article — это тип ActivityStreams, предназначенный для полноценных статей. В зависимости от интерфейса клиента он может отображать превью, а затем по клику показывать всю статью внутри (похоже на предупреждения о содержимом, но с функцией «Читать далее»).

Note — это тип для микроблогинга.

Благодаря поддержке полноценных постов типа Article пользователи смогут напрямую читать, репостить и отвечать на них в своих клиентах ActivityPub.

И, конечно, было бы интересно узнать о вашем плане развития: будете ли вы следовать пути микроблогингового узла ActivityPub или двигаться в сторону федеративных форумов, как Lemmy или Kbin, особенно в свете недавних новостей о Reddit.

1 лайк

@angus, @pmusaraj, вы видели открытый конкурс на получение финансирования NGI Sargasso? Сроки подачи заявок довольно сжатые, но это может быть полезно для дальнейшего развития этого плагина (если у вас ещё нет других планов).

1 лайк

Привет, ребята! Рад сообщить, что вторая фаза работ над этим плагином утверждена. Вот над чем мы уже начали работать с целью релиза примерно через 3,5 месяца.

Поддержка редактирования заметок после публикации

Поддержка восстановления заметок

Поддержка публикации постов как публично, так и только для подписчиков

  • Настройка на уровне категории
  • Подробнее см. адресацию аудитории и документацию Mastodon по полям to/cc
  • Сделать публичные посты настройкой по умолчанию

Улучшение парсинга содержимого заметок

  • Обработка специальных символов (возможно, использование другого парсера). Подробнее.

Поддержка использования статьи вместо заметки в качестве объекта для поста.

  • Настройка на уровне категории

Поддержка принятия активностей в ответ на заметку, созданную на удалённых серверах, и публикации активностей в ответ на заметку, созданную в Discourse.

  • Публикация активностей, касающихся ответов, созданных в Discourse
    • Разрешить пользователям Discourse выступать в роли акторов
    • Создавать объекты заметок для ответов в Discourse (постов)
    • Публиковать соответствующие активности создания/удаления/обновления/отмены для эквивалентных действий в Discourse
  • Принятие активностей, касающихся ответов, созданных на удалённых серверах
    • Стабилизация акторов активностей с удалённых серверов как пользователей Discourse
    • Создание ответов в Discourse (постов) из объектов заметок
    • Преобразование соответствующих активностей создания/удаления/обновления/отмены в эквивалентные действия в Discourse
  • Добавлена настройка категории для переключения между «Только первый пост» (текущее поведение) и «Полная тема», поддерживающая активности ответов.

Поддержка активности «Нравится»

Поддержка верификации пользователями Discourse своей личности в Mastodon, чтобы посты в Discourse, созданные из их твитов (toots), были связаны с их учётной записью в Discourse.

  • Разрешить пользователю выполнить процесс авторизации OAuth в Mastodon с сервером Mastodon, где хранится его учётная запись. Это инициируется из настроек учётной записи пользователя в Discourse.
  • Используя токен доступа Mastodon пользователя Discourse, получить и сохранить AP-идентификатор его учётной записи Mastodon и связать его с учётной записью пользователя в Discourse.
  • Связать все активности в Discourse, соответствующие AP-активностям от актора, имеющего AP-идентификатор пользователя Discourse, с этим пользователем, независимо от того, были ли они выполнены до или после верификации личности пользователя.
15 лайков

Это действительно захватывающе видеть — такой высокий уровень федерации и интерактивности. :tada:

Планируете ли вы какие-либо промежуточные релизы или это будет один большой релиз примерно через 3,5 месяца?

Возможно, будут промежуточные релизы, но я пока не могу ничего обещать в этом плане. Я буду держать вас в курсе по мере продвижения.

2 лайка

Согласен на 110% — здесь много отличных аспектов. :tada:

Есть ли хоть какая-то возможность выпустить хотя бы промежуточную версию по этому вопросу?

Честно говоря, выпуск промежуточной версии с публичными постами по умолчанию был бы немедленно приветствован.

2 лайка

Обратите внимание, что публичная публикация, как я полагаю, будет зависеть от федерации действий редактирования (первый пункт в списке).

1 лайк

На данном этапе я не могу дать никаких обещаний, но вполне возможны промежуточные обновления как для обновления федерации, так и для таргетинга аудитории (публичные публикации).

4 лайка

:eyes:

1 лайк

Это известное ограничение. До тех пор, пока не будет поддерживаться синхронизация правок, плагин блокирует изменения в синхронизируемом контенте, и отключить это поведение через конфигурацию невозможно.

2 лайка

Чтобы было понятно: я пытался отредактировать сообщение здесь, на Meta, и получил эту ошибку.

3 лайка

Ой, извините, что я неправильно понял. По крайней мере, @feature@meta.discourse.org и @announcements@meta.discourse.org здесь федеративно распространяются, и именно это является главной причиной, по которой я не включил эту функцию для Maker Forums…

2 лайка

Да, это первый элемент Фазы 2, над которым я работал. Более того, для него уже создан PR, так что скоро вы получите по этому вопросу некоторое облегчение.

2 лайка