ActivityPub 플러그인

@hellekin 보고해 주셔서 감사합니다. Fediverse의 액터들이 유입되고 이탈하기 때문에 ActivityPub 서비스에서는 항상 일부 요청 실패가 발생합니다. 예를 들어, 다음과 같은 경우입니다.

로그의 상세 수준은 디버깅을 돕기 위해 설정되어 있습니다. 다만 로그가 지나치게 많다면 사이트 설정의 activity pub verbose logging 옵션을 통해 이를 조정할 수 있습니다. 기본값은 꺼져 있습니다.

필요하다면 2단계에서 오류 처리 개선 사항을 검토할 계획이지만, 현재까지 게시하신 스니펫은 예상된 동작으로 보입니다. 즉, 해당 액터들이 실제로 Fediverse에서 더 이상 존재하지 않는 것입니다.

현재 이 플러그인은 Mastodon과 동일한 방식으로 전달 실패를 추적합니다. 구체적으로, 특정 엔드포인트에 대한 실패가 7일간 지속되면 "사용 불가"로 표시되고, 이후 해당 엔드포인트에 대한 요청은 더 이상 시도되지 않습니다.

3개의 좋아요

네, 바로 앞 줄에 명시되어 있듯이요.

맞습니다. 하지만 상태 코드는 410이며, 이는 계정이 이동했을 수 있음을 의미합니다( Tombstone이 있다면 이 조건을 확인하나요?)

1개의 좋아요

아니요. 410 Gone 오류는 리소스가 삭제되었음을 의미합니다. 제가 지금 뭘 놓치고 있는 걸까요?

1개의 좋아요

이 부분을 놓치셨습니다:

@angus님, 안녕하세요.

https://federation.cafe에서 님의 AP 플러그인을 사용하여 완전히 새로운 자체 호스팅 Discourse를 설정했는데, Discourse 에러 로그에서 403 오류가 발생하고 있으며(게시물도 공유되지 않습니다).

하이픈이 문제일 수도 있을까요?

[Discourse Activity Pub] GET request to https://bofh.social/internal/fetch failed: Expected([200, 201, 202, 301, 302, 307, 308]) <=> Actual(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	2:16 pm

생각해 주실 만한 점이 있을까요?

저도 전체 길이의 게시글에 관심이 있습니다. 이 경우 “노트” 대신 “기사” 유형을 사용하는 것이 적절할 것 같습니다.

사실, 공지사항보다는 포럼 연동 기능이 훨씬 더 흥미롭습니다.

이러한 기능에 대한 로드맵이나 기부를 할 수 있는 곳이 있을까요?

안녕하세요 @gme, 플러그인을 테스트해 주셔서 감사합니다.

처음으로 몇 가지 참고할 사항이 있습니다.

  1. MVP 플러그인은 Mastodon을 대상으로 테스트되었습니다. 거기서 Pleroma를 사용하시는 것 같은데, Pleroma가 ActivityPub에 호환되고 Mastodon과 함께 작동한다는 것은 알고 있습니다. 하지만 아직 Pleroma에 대한 지원을 보장하기 위해 어떤 조정(있다면)이 필요한지 자세히 살펴보지는 않았습니다. 그래도 여기서 무슨 일이 일어나고 있는지 궁금합니다.

  2. 요청이 Pleroma 서버의 인증 오류로 인해 실패한 것으로 보입니다(403 오류가 의미하는 바입니다). 인증되지 않은 cURL 요청만으로 해당 엔드포인트를 GET할 수 있으므로, Pleroma 쪽에서 http 인증이 실패하고 있는 것 같습니다.

후자(즉, 2번)를 테스트하기 위해, 가능하다면 Pleroma 로그(그 서버의 관리자이기도 한 것 같네요?)를 확인해 보시고, 해당 쪽에서 더 자세한 정보를 얻을 수 있는지 살펴봐 주시겠어요?

피드백 감사합니다 @bmann. 여기서 염두에 두고 있는 사용 사례를 좀 더 자세히 설명해 주실 수 있을까요? 가능하다면 예시를 들어주시면 좋겠습니다.

이 토픽은 개발 동향을 파악하는 데 가장 좋은 곳입니다. 2단계 계획이 확정되면 여기서 공유하겠습니다. 그 사이, 플러그인을 사용하거나 사용하고자 하는 구체적인 사용 사례를 공유하는 것이 도움이 되는 가장 좋은 방법입니다.

5개의 좋아요

사용 사례는 Discourse를 더 완전한 AP(액티비티퍼블릭) 노드로 활용하는 것입니다. AP에 콘텐츠를 게시하는 더 쉬운 방법들이 많이 있습니다(예: 카테고리 RSS 피드와 Zapier 또는 Buffer 사용) — 하지만 더 완전한 AP 기능을 개발하려면 플러그인/통합으로만 가능합니다.

Article은 전체 기사를 위한 ActivityStream 유형입니다. 클라이언트 인터페이스에 따라 미리보기가 표시되고, 클릭 시 전체 기사가 인라인으로 표시됩니다(콘텐츠 경고와 유사하지만, '더 읽기’와 같은 방식).

Note는 마이크로블로깅 유형입니다.

완전한 Article 게시물을 제공하면, 사용자는 AP 클라이언트에서 직접 읽기 / 부스트 / 답글을 남길 수 있습니다.

물론, 최근 Reddit 뉴스를 고려할 때, 마이크로블로깅 AP 인스턴스를 따를지, 아니면 Lemmy나 Kbin처럼 연동된 포럼 방향으로 나아갈지에 대한 로드맵에 대해 듣는 것도 흥미로울 것입니다.

1개의 좋아요

@angus, @pmusaraj NGI Sargasso 자금 지원 공개 모집을 보셨나요? 기한이 꽤 짧지만, 이 플러그인의 추가 개발에 도움이 될 수 있을 것 같습니다(이미 다른 계획이 없다면요).

1개의 좋아요

여러분, 이 플러그인의 2단계 작업이 승인되었다는 소식을 전하게 되어 기쁩니다. 이미 작업을 시작했으며, 약 3.5개월 내에 출시하는 것을 목표로 하고 있습니다.

노트 게시 후 편집 지원

노트 복원 지원

게시물을 팔로워 전용이 아닌 공개적으로 게시하는 것 지원

  • 카테고리 수준 설정
  • Audience Targeting 및 Mastodon의 to/cc 문서 참고
  • 기본값을 공개 게시물로 변경

노트 콘텐츠 파싱 개선

  • 특수 문자 처리 (다른 파서를 사용할 수 있음). 자세히 보기.

게시물 객체로 Note 대신 Article 사용 지원

  • 카테고리 수준 설정

원격에서 작성된 노트에 대한 답글 Activity 수신 및 Discourse에서 작성된 노트에 대한 답글 Activity 게시 지원

  • Discourse에서 작성된 답글에 관한 Activity 게시
    • Discourse 사용자를 Actor로 허용
    • Discourse 답글(게시물)에 대한 Note 객체 생성
    • 해당 Discourse 동작에 해당하는 Create/Delete/Update/Undo Activity 게시
  • 원격에서 작성된 답글에 관한 Activity 수신
    • 원격 서버의 Activity Actor를 Discourse 사용자로 스테이징
    • Note 객체로부터 Discourse 답글(게시물) 생성
    • 해당 Create/Delete/Update/Undo Activity를 이에 해당하는 Discourse 동작으로 변환
  • 현재 상태인 첫 번째 게시물 전용과 답글 Activity를 지원하는 “전체 주제” 사이를 전환하는 카테고리 설정 추가

Like Activity 지원

Discourse 사용자가 Mastodon에서 신원을 확인하여, 그들의 Toot에서 생성된 Discourse 게시물이 해당 사용자의 Discourse 사용자 계정과 연결되도록 지원

  • 사용자가 계정이 저장된 Mastodon 서버와 Mastodon OAuth Authorization 플로우를 수행할 수 있도록 허용. 이는 사용자의 Discourse 계정 설정에서 시작됩니다.
  • Discourse 사용자의 Mastodon 액세스 토큰을 사용하여 해당 Mastodon 계정의 AP ID를 조회하고 저장하며, 이를 사용자의 Discourse 계정과 함께 저장합니다.
  • 사용자가 신원을 확인하기 전이든 후이든, 해당 Discourse 사용자의 AP ID를 가진 Actor로부터의 AP Activity와 관련된 모든 Discourse 활동을 해당 Discourse 사용자와 연결합니다.
15개의 좋아요

정말 기대되는 소식입니다 — 연동(federation)과 상호작용 수준이 정말 높네요. :tada:

중간 릴리스를 계획하고 계신가요, 아니면 약 3.5개월 후에 한 번에 큰 릴리스로 나올 예정인가요?

중간 릴리스가 몇 번 나올 수는 있지만, 그 부분은 아직 확답을 드리기 어렵습니다. 진행 상황에 따라 계속 업데이트해 드리겠습니다.

2개의 좋아요

110% 동의합니다 - 여기에는 많은 훌륭한 측면이 포함되어 있습니다. :tada:

이 분야에서 중간 단계의 릴리스가 나올 가능성은 없을까요?

솔직히 말하면, “공개 게시물을 기본값으로” 하는 중간 릴리스가 있다면 즉시 환영할 것입니다.

2개의 좋아요

공개 게시는 편집 작업의 연동(목록의 첫 번째 항목)에 의존할 것이라고 생각합니다.

1개의 좋아요

현재 단계에서는 어떤 약속도 드릴 수 없지만, 업데이트 연동과 대상 사용자 타겟팅(공개 게시)에 대해 중간 업데이트가 있을 수도 있습니다.

4개의 좋아요

:eyes:

1개의 좋아요

알려진 한계입니다. 연동된 편집(federating edits)이 지원될 때까지 이 플러그인은 연동된 콘텐츠에 대한 편집을 차단하며, 이를 비활성화할 수 있는 설정은 없습니다.

2개의 좋아요

정확히 말하면, _메타의 이 게시물_을 편집하려고 하다가 이 오류가 발생했습니다.

3개의 좋아요

아, 오해한 줄 알았네요. @feature@meta.discourse.org와 @announcements@meta.discourse.org는 여기서 연동(federate)되고 있긴 하지만, 이 선택이 제가 Maker Forums에서 이 기능을 활성화하지 않은 가장 큰 이유입니다…

2개의 좋아요

네, 이건 제가 작업한 2단계의 첫 번째 항목입니다. 사실 이미 관련 PR이 올라와 있으니, 곧 그 부분에서 좀 안도하실 수 있을 겁니다.

2개의 좋아요