발행 후 보고된 오류나 이슈는 정오표를 꼭 확인해 주세요.
또한 번역본도 참고하세요.
Copyright © 2018 W3C® (MIT, ERCIM, Keio, Beihang). W3C liability, trademark and permissive document license rules apply.
ActivityPub 프로토콜은 [ActivityStreams] 2.0 데이터 포맷을 기반으로 한 분산형 소셜 네트워킹 프로토콜입니다. 이 프로토콜은 클라이언트에서 서버로의 API를 통해 콘텐츠를 생성, 수정, 삭제할 수 있으며, 알림 및 콘텐츠 전달을 위한 연합 서버 간 API도 제공합니다.
이 섹션은 이 문서가 발행될 당시의 상태를 설명합니다. 다른 문서가 이 문서를 대체할 수 있습니다. 현재 W3C 발행물 목록 및 이 기술 보고서의 최신 개정판은 W3C 기술 보고서 인덱스 https://www.w3.org/TR/에서 확인할 수 있습니다.
이 문서는 소셜 웹 워킹 그룹에 의해 권고안으로 발행되었습니다.
모든 관심 있는 분들은 워킹 그룹의 이슈 트래커를 통해 구현 및 버그 제보와 의견을 남길 수 있습니다. 해당 내용은 소셜 웹 커뮤니티 그룹에서 논의되고, 차후 본 사양의 개선에 반영될 수 있습니다.
워킹 그룹의 구현 보고서를 참조하세요.
이 문서는 W3C 회원, 소프트웨어 개발자, 기타 W3C 그룹 및 관심자들의 검토를 받았으며, W3C 이사의 승인을 얻어 W3C 권고안으로 채택되었습니다. 이 문서는 안정적인 문서이며, 참고 자료나 다른 문서에서 인용하여 사용할 수 있습니다. W3C의 권고안 마련 목적은 이 사양의 주목도를 높이고 광범위한 배포를 촉진하는 것입니다. 이는 웹의 기능성과 상호운용성을 강화합니다.
이 문서는 W3C 특허 정책에 따라 운영되는 그룹에서 작성되었습니다. W3C는 본 그룹의 산출물과 관련한 공개 특허 공개 목록을 관리합니다. 해당 페이지에는 특허 공개 방법에 대한 안내도 포함되어 있습니다. 당사자가 실제로 특허를 알고 있고, 그 특허가 본질적 권리청구(Essential Claim(s))를 포함한다고 생각한다면, W3C 특허 정책 제 6조에 따라 공개해야 합니다.
이 문서는 2017년 3월 1일 W3C 프로세스 문서의 관리를 받습니다.
ActivityPub은 두 계층을 제공합니다:
ActivityPub 구현체는 이 중 하나만 구현할 수도 있고, 둘 다 구현할 수도 있습니다. 하지만 둘 중 하나를 구현했다면, 다른 하나도 구현하는 것이 많은 단계를 추가하지 않고도 가능하고, 둘 다 구현하면 (귀하의 웹사이트가 분산형 소셜 웹의 일부가 되고, 다양한 소셜 웹사이트에서 동작하는 클라이언트와 클라이언트 라이브러리를 활용할 수 있어) 많은 이점이 있습니다.
ActivityPub에서 사용자는 서버의 계정을 통해 "액터"로 표현됩니다. 사용자의 여러 서버 계정은 각각 다른 액터에 대응합니다. 모든 액터는 다음을 가집니다:
inbox: 세상으로부터 메시지를 받는 방법outbox: 다른 이들에게 메시지를 보내는 방법
이들은 엔드포인트, 즉 ActivityPub 액터의 ActivityStreams 설명에 나열된 URL들입니다. (ActivityStreams에 대해선 뒤에서 다룹니다.)
친구 Alyssa P. Hacker의 레코드 예시는 다음과 같습니다:
{"@context": "https://www.w3.org/ns/activitystreams",
"type": "Person",
"id": "https://social.example/alyssa/",
"name": "Alyssa P. Hacker",
"preferredUsername": "alyssa",
"summary": "MIT 출신, Lisp 애호가",
"inbox": "https://social.example/alyssa/inbox/",
"outbox": "https://social.example/alyssa/outbox/",
"followers": "https://social.example/alyssa/followers/",
"following": "https://social.example/alyssa/following/",
"liked": "https://social.example/alyssa/liked/"}
ActivityPub은 단어집으로 [ActivityStreams]를 사용합니다. ActivityStreams는 소셜 네트워크 내 활동과 콘텐츠를 표현하는 데 필요한 공통 용어들을 모두 제공하므로 아주 유용합니다. 대부분의 경우 ActivityStreams에 필요한 모든 단어가 이미 들어 있지만, 만약 부족하다면 [JSON-LD]를 통해 ActivityStreams를 확장할 수도 있습니다. JSON-LD가 무엇인지 안다면, 그에 따른 연결 데이터 방법들의 장점도 이용할 수 있습니다. 만약 모른다 해도, JSON-LD 문서와 ActivityStreams는 일반적인 간단한 JSON처럼 이해할 수 있습니다. (확장을 추가할 때 JSON-LD의 진가가 드러납니다.)
좋아요. Alyssa는 친구들과 대화하고 싶어 하고, 친구들도 그녀와 대화하고 싶어 할 것입니다! "inbox"와 "outbox"가 이런 부분을 도와줍니다. GET과 POST 방식에 따라 동작이 다릅니다. 어떻게 동작하는지 살펴봅시다:
네, 요약하자면:
물론, 마지막 방법(타인의 outbox에서 GET)만으로 모든 메시지를 받게 하면 연합 프로토콜로서 매우 비효율적일 것입니다! 실제 연합은 보통 서버가 액터의 메시지를 다른 서버의 액터의 inbox로 POST함으로써 일어납니다.
예시를 살펴봅시다! Alyssa가 친구 Ben Bitdiddle과 연락하려 합니다. 최근 Ben에게 책을 빌려줬고, 돌려받고 싶은 것입니다. Alyssa가 작성한 ActivityStreams 메시지는 다음과 같습니다:
{"@context": "https://www.w3.org/ns/activitystreams",
"type": "Note",
"to": ["https://chatty.example/ben/"],
"attributedTo": "https://social.example/alyssa/",
"content": "혹시, 내가 빌려준 책 다 읽었니?"}
이 노트는 Ben에게 전달됩니다. Alyssa는 이를 자신의 outbox에 POST합니다.
이 메시지는 액티비티가 아닌 객체이기 때문에, 서버는 새 객체 생성임을 인식하고 이를 Create 액티비티로 감쌉니다. (ActivityPub의 액티비티는 일반적으로 액터가 어떤 객체에 행위를 하는 패턴을 따릅니다. 여기서 행위는 Person이 Note 객체를 Create하는 것입니다.)
{"@context": "https://www.w3.org/ns/activitystreams",
"type": "Create",
"id": "https://social.example/alyssa/posts/a29a6843-9feb-4c74-a7f7-081b9c9201d3",
"to": ["https://chatty.example/ben/"],
"actor": "https://social.example/alyssa/",
"object": {"type": "Note",
"id": "https://social.example/alyssa/posts/49e2d03d-b53a-4c4c-a95c-94a6abf45a19",
"attributedTo": "https://social.example/alyssa/",
"to": ["https://chatty.example/ben/"],
"content": "혹시, 내가 빌려준 책 다 읽었니?"}}
Alyssa의 서버는 Ben의 ActivityStreams 액터 객체를 찾아 그에 해당하는 inbox 엔드포인트를 찾아 객체를 Ben의 inbox로 POST합니다.
엄밀히 따지면 이는 두 단계로 나뉩니다... 하나는 클라이언트-서버 통신, 하나는 서버-서버 통신(연합)을 위한 것입니다. 그러나 이 예시에서는 두 단계를 연속 사용하므로, 아웃박스→인박스로의 메시지 전달을 추상적으로 하나의 흐름으로 볼 수 있습니다:
멋지네요! 시간이 지난 후 Alyssa는 새로운 메시지가 왔는지 확인합니다. 그녀의 휴대폰이 GET으로 inbox를 폴링하고, 친구들이 올린 고양이 동영상과 언니가 올린 조카 사진 속에 다음과 같은 메시지가 보입니다:
{"@context": "https://www.w3.org/ns/activitystreams",
"type": "Create",
"id": "https://chatty.example/ben/p/51086",
"to": ["https://social.example/alyssa/"],
"actor": "https://chatty.example/ben/",
"object": {"type": "Note",
"id": "https://chatty.example/ben/p/51085",
"attributedTo": "https://chatty.example/ben/",
"to": ["https://social.example/alyssa/"],
"inReplyTo": "https://social.example/alyssa/posts/49e2d03d-b53a-4c4c-a95c-94a6abf45a19",
"content": "<p>아, 미안! 내일 꼭 돌려줄게.</p>
<p>레지스터 머신 부분 복습 좀 했어, 오랜만이라서.</p>"}}
Alyssa는 안도하며 Ben의 게시물에 '좋아요'를 누릅니다:
{"@context": "https://www.w3.org/ns/activitystreams",
"type": "Like",
"id": "https://social.example/alyssa/posts/5312e10e-5110-42e5-a09b-934882b3ecec",
"to": ["https://chatty.example/ben/"],
"actor": "https://social.example/alyssa/",
"object": "https://chatty.example/ben/p/51086"}
Alyssa는 이 메시지를 outbox에 POST합니다. (액티비티이므로 서버는 따로 Create 객체로 포장하지 않아도 됨을 인지합니다.)
기분이 좋아진 Alyssa는 팔로워들에게 공개 메시지를 포스팅하기로 합니다. 잠시 후 그녀의 팔로워 컬렉션의 모든 멤버뿐 아니라 "Public" 그룹이 지정되어 있기 때문에 누구나 이 메시지를 읽을 수 있습니다.
{"@context": "https://www.w3.org/ns/activitystreams",
"type": "Create",
"id": "https://social.example/alyssa/posts/9282e9cc-14d0-42b3-a758-d6aeca6c876b",
"to": ["https://social.example/alyssa/followers/",
"https://www.w3.org/ns/activitystreams#Public"],
"actor": "https://social.example/alyssa/",
"object": {"type": "Note",
"id": "https://social.example/alyssa/posts/d18c55d4-8a63-4181-9745-4e6cf7938fa1",
"attributedTo": "https://social.example/alyssa/",
"to": ["https://social.example/alyssa/followers/",
"https://www.w3.org/ns/activitystreams#Public"],
"content": "친구에게 책을 빌려주는 건 기쁨, 다시 돌려받는 건 더 큰 기쁨! :)"}}
비규범적(non-normative)으로 표시된 섹션 외에도, 이 명세서의 모든 작성 지침, 다이어그램, 예시, 주석은 비규범적입니다. 이외 모든 내용은 규범적(normative)입니다.
키워드 MAY, MUST, MUST NOT, SHOULD, SHOULD NOT는 [RFC2119]에 명시된 대로 해석해야 합니다.
이 명세서는 긴밀하게 연관되고 상호작용하는 두 개의 프로토콜을 정의합니다:
ActivityPub 명세는 이 두 프로토콜 중 하나라도 구현하면 다른 하나를 지원하는 데 추가 비용이 거의 없도록 설계됐습니다. 그러나 서버는 여전히 둘 중 하나만 구현할 수도 있습니다. 이로 인해 아래 세 가지 적합성 클래스가 생깁니다:
명세의 특정 부분이 연합 프로토콜 구현에만 적용될 때는 이를 명확히 표시합니다. 또한, 어떤 요구사항이 명시될 때는 그것이 클라이언트(클라이언트-서버 프로토콜), 서버(클라이언트-서버 프로토콜), 혹은 서버-서버 프로토콜의 송신/수신 서버에 적용되는지 분명히 나타냅니다.
객체는 [ActivityStreams]와 ActivityPub 모두의 핵심 개념입니다. 객체는 종종 액티비티로 감싸여 컬렉션 스트림에 포함되며, 컬렉션 또한 객체의 하위 클래스입니다. [Activity-Vocabulary] 문서의 Core Classes 를 특히 참고하세요. ActivityPub은 이 단어집의 맵핑을 매우 정확히 따릅니다.
ActivityPub은 ActivityStreams가 제공하는 용어 외에 몇 가지 용어를 추가로 정의합니다.
이 용어들은 ActivityPub
JSON-LD context
(https://www.w3.org/ns/activitystreams)에 제공됩니다.
구현자는 객체 정의에 ActivityPub context를 권장적으로
포함해야 하며,
필요하다면 선택적으로 추가 컨텍스트도 넣을 수 있습니다.
ActivityPub은 ActivityStreams와 동일한 URI / IRI 규칙을 따릅니다.
서버는 권장적으로 수신한 콘텐츠를 검증하여 콘텐츠 위조 공격을 막아야 합니다. (최소한 원본에서 받은 객체가 동일하게 나타나는지 확인해야 하며, 가능하다면 서명 검증 등 추가 검증 기법을 적용하는 것이 좋습니다.) 이 문서는 특정 검증 메커니즘을 권위 있게 명시하지 않지만, 보안 고려사항에서 제안 및 좋은 관행을 참고하세요.
{
"@context": "https://www.w3.org/ns/activitystreams",
"type": "Like",
"actor": "https://example.net/~mallory",
"to": ["https://hatchat.example/sarah/",
"https://example.com/peeps/john/"],
"object": {
"@context": {"@language": "en"},
"id": "https://example.org/~alice/note/23",
"type": "Note",
"attributedTo": "https://example.org/~alice",
"content": "I'm a goat"
}
}
id의 경로를 실제로 dereference(확인)해서
존재하는 객체인지, 유효한 객체인지, 위조되지 않았는지 확인해야 합니다.
(이 예시에서, Mallory가 Alice가 쓴 것처럼 위장할 수 있기 때문입니다.)
[ActivityStreams]의 모든 객체는 전역적으로 고유한 식별자를 가져야 합니다. ActivityPub에서는 이 요구를 확장하여, ActivityPub 프로토콜을 통해 배포되는 모든 객체는 반드시 고유한 전역 식별자를 가져야 하며, 의도적으로 일시적인 객체(예: 조회 불가능한 단명 채팅 메시지나 게임 알림 등)가 아니라면 반드시 필요한 조건입니다. 이런 식별자는 아래 둘 중 하나여야 합니다:
null 객체로 명시적으로 지정된 ID.
이는 익명 객체(상위 컨텍스트의 일부임을 의미).
서버-서버 통신으로 게시되는 액티비티에는, 그 활동이 명시적으로 일시적이 아닌 한
반드시 식별자를 제공해야 합니다.
하지만 클라이언트-서버 통신의 경우, outbox로 게시된 객체에 id가 지정되지 않았을 때
서버는 해당 액터의 네임스페이스에 객체 ID를 할당해 게시 객체에 붙여줘야 합니다(권장).
모든 객체에는 다음 속성이 있습니다:
id는 생략 가능).
HTTP GET 메소드는 객체의 id 속성에 대해 dereference될 수 있으며,
이를 통해 액티비티를 조회할 수 있습니다.
서버는 [RFC7231]에 정의된 HTTP 콘텐츠 협상을
선택적으로 사용할 수 있지만,
application/ld+json; profile="https://www.w3.org/ns/activitystreams" 미디어 타입으로 요청받을 경우
반드시 ActivityStreams 객체 표현(리턴값)을 제공해야 하고,
또한 가능하다면 application/activity+json에도
ActivityStreams 표현을 제공해야 합니다.
클라이언트는 액티비티를 조회하려면 Accept 헤더를
application/ld+json; profile="https://www.w3.org/ns/activitystreams"
미디어 타입으로 반드시 지정해야 합니다.
서버는 위 요구를 충족하지 않는 요청에 대해 다른 동작을 구현할 수 있습니다. (예: 추가적인 기존 프로토콜 지원, 하나의 URI로 HTML과 ActivityStreams 포맷 모두 제공 등)
서버는 B.1 인증 및 권한 부여에서 규정한 대로 권한 인증을 요구할 수 있으며, 추가적인 권한 정책을 자체적으로 둘 수도 있습니다. 서버는 권한 검증에 실패한 요청을 적절한 HTTP 오류 코드나, 객체 존재 자체를 비공개로 하고 싶으면 403 Forbidden 오류 코드로 실패 처리해야 합니다. 비공개 객체 존재 유무 자체도 노출하고 싶지 않으면, 원 서버는 404 Not Found 상태코드로 응답할 수도 있습니다.
[Activity-Vocabulary]가 정의한
모든 속성 외에도, ActivityPub은 Object에 source 속성을 추가로 제공합니다.
source 속성은 content 마크업이
어디서 파생되었는지, 즉 원본 정보나 향후 클라이언트 수정 시 활용할 수 있도록
표시합니다.
일반적으로 source에서 content로의 변환은
클라이언트가 담당하며(그 반대는 아님),
source의 형식을 직접 지원할 수 있는지는 클라이언트에 따라 다릅니다.
source 값은 content와 mediaType 필드를 갖는
별도의 객체로, 원본 정보를 담습니다.
{
"@context": ["https://www.w3.org/ns/activitystreams",
{"@language": "en"}],
"type": "Note",
"id": "http://postparty.example/p/2415",
"content": "<p>나는 <em>딸기를</em> 정말 좋아한다!</p>",
"source": {
"content": "나는 *정말* 딸기를 좋아한다!",
"mediaType": "text/markdown"}
}
일반적으로, 사용자가 글을 쓸 때 썼던 원본(source) 포맷 그대로
다시 수정할 수 있게 하는 것이 최선입니다.
하지만 각 클라이언트가 모든 원본 타입을 좋은 인터페이스로
제공할 수 있는 것은 아닙니다.
클라이언트는 대체로 source에서 content로 변환만 할 뿐,
역방향은 잘 못 합니다.
어떤 클라이언트가 content 마크업을 에디터에 보여주고
source는 무시할 수도 있는데,
이 경우 다음번 수정 시 원본 source를 잃게 됩니다.
이런 동작을 할 땐 최소한 "원본 포맷을 이해하지 못해 무시하니 편집 시 기존 원본이 사라질 수 있음"이라는 경고는
꼭 부드럽게 표시해야 합니다.
예를 들어, Alyssa P. Hacker는 Org mode(Org mode)를 활용해
ActivityPub 블로그 글을 Emacs 클라이언트로 씁니다.
나중엔 휴대폰 클라이언트로 글을 수정하려는데,
이 앱은 text/x-org가 뭔지 모르고 HTML로 변환도 못 하니,
content만 빈 텍스트박스에 띄워줍니다.
편집란 위에는 "이 글은 우리가 지원하지 않는 마크업 언어로 작성되었습니다. 수정하면 원본을 잃게 됩니다."라는 경고가 뜹니다.
Alyssa는 작은 오타 정도는 원본 org-mode 마크업을 잃으면서 수정할 가치가 없다고 판단해,
집에 가서 나중에 수정하기로 합니다.
ActivityPub의 액터는 일반적으로
ActivityStreams 액터 유형 중 하나이지만,
반드시 그럴 필요는 없습니다.
예를 들어,
Profile 객체를 액터로 사용할 수도 있고, ActivityStreams 확장 타입일 수도 있습니다.
액터는 ActivityPub에서 다른 오브젝트처럼
조회됩니다.
다른 ActivityStreams 객체와 마찬가지로 액터도 id를 가지며,
이 값은 URI입니다.
만약 사용자 인터페이스(로그인 폼 등)에 바로 입력되는 경우에는
이름을 좀 더 간단히 입력할 수 있도록 지원하는 것이 좋습니다.
이를 위해, 다음과 같이 ID 정규화가 권장됩니다:
example.org/alice/)을 빠뜨린 것으로 보이는 경우, 클라이언트는 선택적으로
기본 스킴(가능하면 https)을 자동으로 붙여줄 수 있습니다.
액터의 URI를 식별하면, dereference(실제 조회)해야 합니다.
액터 객체는 3.1 Object 식별자에서 요구하는 속성 이외에, 아래 속성들을 반드시 포함해야 합니다:
OrderedCollection에 대한 참조입니다.
자세한 내용은 5.2 Inbox를 참조하세요.
OrderedCollection입니다.
5.1 Outbox를 참고하세요.
구현에서는 아래 속성도 권장적으로 제공해야 합니다:
구현에서는 아래 속성도 선택적으로 제공할 수 있습니다:
{
"@context": ["https://www.w3.org/ns/activitystreams",
{"@language": "ja"}],
"type": "Person",
"id": "https://kenzoishii.example.com/",
"following": "https://kenzoishii.example.com/following.json",
"followers": "https://kenzoishii.example.com/followers.json",
"liked": "https://kenzoishii.example.com/liked.json",
"inbox": "https://kenzoishii.example.com/inbox.json",
"outbox": "https://kenzoishii.example.com/feed.json",
"preferredUsername": "kenzoishii",
"name": "石井健蔵",
"summary": "この方はただの例です",
"icon": [
"https://kenzoishii.example.com/image/165987aklre4"
]
}
구현에서는 아래 속성도 선택적으로 추가할 수 있습니다:
endpoints 매핑에는 아래 속성을 선택적으로 포함할 수 있습니다:
x-www-form-urlencoded의 id 파라미터에 요청 오브젝트의
id를 넣어 POST합니다.
sharedInbox 엔드포인트는 권장적으로 Public 특수
컬렉션에 주소지정된 오브젝트들이 담긴 공개 OrderedCollection 오브젝트여야 합니다.
sharedInbox를 읽을 때 절대 Public이 아닌
오브젝트는 표시하지 않아야 합니다.
ActivityPub의 상위 단어집인 [ActivityStreams]의 모든 속성은 ActivityPub 액터에서 사용될 수 있습니다. ActivityStreams 속성 중에는 ActivityPub 구현에서 어떻게 쓰이는지 보여주기 위한 예시가 될 만한 속성들이 있습니다.
id와 다를 때).
name, preferredUsername, summary 등 자연어 값을 담는 속성들은
ActivityStreams에서 정의된 자연어 지원 규칙을 사용합니다.
[ActivityStreams]에서 컬렉션 개념이 정의되고, ActivityPub은 특별 동작이 지정된 여러 컬렉션을 정의합니다. ActivityPub은 ActivityStreams 페이징도 활용해 대량 오브젝트를 순회할 수 있습니다.
이 중 일부 컬렉션은
OrderedCollection임이 명시되고,
나머지는
Collection
혹은
OrderedCollection이 될 수 있습니다.
OrderedCollection은 반드시
항상 역순(최신→과거) 순서로 표시되어야 합니다.
역순 정렬 기준이 무엇인지는 구현에 맡깁니다. 예를 들어 대부분의 SQL 스타일 데이터베이스는 증가하는 정수 ID를 이용해, 삽입 순서를 처리할 수 있습니다. 다른 데이터베이스에서는 삽입 시간을 나타내는 타임스탬프를 더 선호할 수도 있습니다. 어떤 속성이든 상관없으나, 새 항목이 앞에 오도록 순서는 반드시 유지되어야 합니다. 자주 바뀌는 속성(예: "최근 수정시각" 등)을 정렬 기준으로 써서는 안 됩니다.
outbox는 액터의 outbox 프로필 속성에서 찾을 수 있습니다.
outbox는 반드시
OrderedCollection이어야 합니다.
outbox 스트림에는 사용자가 발행한 액티비티가 들어 있는데, 실제로 액티비티를 볼 수 있는지 여부는 요청자의 권한에 따라 달라집니다(즉, 읽는 사람의 권한에 따라 outbox 내용이 필터링됨). 만약 Authorization 없이 요청한다면, 서버는 Public으로 주소지정된 게시물만 응답해야 합니다. 실제로는 서버마다 사용자가 볼 수 있는 아이템 수를 제한하거나 다르게 구현할 수 있습니다.
outbox는 HTTP POST 요청을 받으며, 동작 방식은 클라이언트-서버 상호작용에서 설명합니다.
inbox는 액터의 inbox 프로필 속성에서 찾을 수 있습니다.
inbox도 반드시
OrderedCollection이어야 합니다.
inbox 스트림에는 액터가 받은 모든 액티비티가 들어 있습니다. 서버는 권장적으로 요청자 권한에 따라 내용을 필터링해야 합니다. 일반적으로 inbox 소유자는 자신의 모든 내용을 볼 수 있지만, 접근 제어에 따라 일부는 공개·일부는 권한 인증이 필요할 수 있습니다.
서버는 inbox에 반환하는 액티비티의 중복을 반드시 제거해야 합니다.
예를 들어 액티비티가 한 액터의 팔로워와 특정 팔로워 모두에 보내지는 경우 등, 서버가 반복 수신자 목록을 중복 제거하지 못하면 중복이 발생할 수 있습니다.
이때 id가 같은 액티비티를 걸러내야 합니다.
연합 서버의 액터는 inbox에서 HTTP POST 요청을 받을 수 있으며, 동작 방식은 Delivery에서 설명합니다. 비연합 서버는 권장적으로 POST 요청을 받으면 405(Method Not Allowed)로 응답해야 합니다.
모든 액터는 followers 컬렉션을 권장적으로
가져야 합니다.
이는 액터에 대해
Follow
액티비티를 보낸, 즉 팔로우한 주체의 목록으로,
부수 효과로 추가된 항목입니다.
해당 액터를 팔로우하는 모든 액터의 목록을 여기에 찾을 수 있습니다.
followers 컬렉션은 반드시
OrderedCollection
또는
Collection
여야 하며, 로그인된 사용자의 권한에 따라, 혹은 인증이 없는 경우 적절하게 필터링 가능합니다.
팔로우 액티비티는 일반적으로 액터가 생성한 오브젝트를 볼 수 있도록 요청하는 것입니다. 따라서 Followers 컬렉션은 알림의 기본 전달 대상로 적합합니다.
모든 액터는 following 컬렉션을 권장적으로 가져야 합니다.
이는 액터가 팔로우한 모든 대상의 목록으로,
부수 효과로 추가된 항목입니다.
following 컬렉션은 반드시
OrderedCollection
또는
Collection
여야 하며, 로그인된 사용자의 권한에 따라, 혹은 인증이 없는 경우 적절하게 필터링 가능합니다.
모든 액터는 liked 컬렉션을 선택적으로 가질 수 있습니다.
이는 액터의 모든 Like 액티비티가 부수 효과로 추가되어 모인 목록입니다.
liked 컬렉션은 반드시
OrderedCollection
또는
Collection
여야 하며, 로그인된 사용자의 권한에 따라, 혹은 인증이 없는 경우 적절하게 필터링 가능합니다.
[ActivityStreams] 컬렉션 및 오브젝트 외에도,
Activity는 특별한 "public" 컬렉션(https://www.w3.org/ns/activitystreams#Public)에 주소지정할 수도 있습니다.
예시:
{
"@context": "https://www.w3.org/ns/activitystreams",
"id": "https://www.w3.org/ns/activitystreams#Public",
"type": "Collection"
}
이 특별 URI로 주소지정된 Activity는 누구나 인증 없이 접근할 수 있어야 합니다.
구현에서는 "public" 특수 컬렉션으로의 전달을 절대 허용하면 안 됩니다.
해당 컬렉션은 실제 activity를 받는 것이 불가능합니다.
다만, 액터는
sharedInbox 엔드포인트를 두고
공개 글(혹은 팔로워 전용 글)의 효율적 일괄 배포에 이용할 수 있습니다.
자세한 내용은 7.1.3 Shared Inbox Delivery 참고.
ActivityStreams 오브젝트를 ActivityStreams JSON-LD 컨텍스트로 compact(압축)하면,
https://www.w3.org/ns/activitystreams#Public이 Public 또는
as:Public로 변환될 수 있습니다.
이는 Public 컬렉션의 유효한 표현입니다.
ActivityStreams 오브젝트를 단순 JSON으로 처리하고 JSON-LD 툴링 없이 Activity를 로컬 컨텍스트로 변환하지 않는 구현도 이 부분을 인지하고 세
가지 표현 모두 허용해야 합니다.
모든 오브젝트는 likes 컬렉션을 선택적으로 가질 수 있습니다.
이는 이 오브젝트가 object 속성에 명시된 모든 Like 액티비티 목록으로,
부수 효과로 추가됩니다.
likes 컬렉션은 반드시
OrderedCollection
또는
Collection
여야 하며, 로그인된 사용자의 권한에 따라, 혹은 인증이 없는 경우 적절하게 필터링 가능합니다.
likes
컬렉션과 비슷하지만 다른 liked 컬렉션을 혼동하지 말아야 합니다.
요약하자면:
Like 액티비티들의 컬렉션으로,
outbox 전달의 부수 효과로 추가됩니다.
Like 액티비티의 컬렉션으로,
inbox 전달의 부수 효과로 추가됩니다.
[ActivityStreams]에 의해 정의된 Activity는 소셜 그래프 내에서 콘텐츠를 생성, 수정, 공유하는 핵심 메커니즘입니다.
클라이언트-서버 상호작용은 클라이언트가 액터의 outbox에 Activity를 포스팅함으로써 이루어집니다.
이를 위해, 클라이언트는 프로필에서 액터의 outbox URL을 반드시
발견해야 하며,
그 후 해당 URL에 application/ld+json; profile="https://www.w3.org/ns/activitystreams" 타입의 HTTP
POST 요청을 반드시 보내야 합니다.
서버는 application/activity+json의 Content-Type 또는 Accept 헤더를
클라이언트-서버 상호작용에서 application/ld+json; profile="https://www.w3.org/ns/activitystreams"와 동등하게 해석할 수 있습니다.
요청은 outbox의 소유자인 사용자의 자격 증명으로 반드시 인증되어야 합니다.
POST 요청의 본문에는 하나의 Activity(내부에 오브젝트가 포함될 수도 있음) 또는
서버가 Create activity로 감쌀 수 있는 단일 비-Activity 오브젝트가 필수로 들어가야 합니다.
POST /outbox/ HTTP/1.1
Host: dustycloud.org
Authorization: Bearer XXXXXXXXXXX
Content-Type: application/ld+json; profile="https://www.w3.org/ns/activitystreams"
{
"@context": ["https://www.w3.org/ns/activitystreams",
{"@language": "en"}],
"type": "Like",
"actor": "https://dustycloud.org/chris/",
"name": "Chris liked 'Minimal ActivityPub update client'",
"object": "https://rhiaro.co.uk/2016/05/minimal-activitypub",
"to": ["https://rhiaro.co.uk/#amy",
"https://dustycloud.org/followers",
"https://rhiaro.co.uk/followers/"],
"cc": "https://e14n.com/evan"
}
Activity의 id 속성에 값이 포함되어 제출될 경우, 서버는 이를 무시하고 새 id를 새로 생성해야 합니다.
서버는 반드시 201 Created HTTP 코드를 반환하고, Activity가
일시적(transient)이 아닌 경우 반드시 새 id를 Location 헤더에
포함시켜야 합니다.
HTTP/1.1 201 Created
Location: https://dustycloud.org/likes/345
서버는 반드시 ActivityStreams 오브젝트에서 bto와 bcc 속성이
있으면 이를 전달 전 제거해야 하며,
단, delivery에서 수신자를 결정할 때는 bto / bcc에 원래 저장된 주소 정보를 반드시 사용해야 합니다.
서버는 그런 다음 생성된 Activity를 outbox 컬렉션에 반드시 추가해야 합니다. Activity의 종류에 따라 서버는 추가적인 부수 효과를 실행해야 할 수 있습니다. (단, Activity가 outbox에 언제 표시되는지는 보장되지 않으며 지연 후 나타나거나 삭제될 수도 있습니다.) 각 Activity별 부수 효과는 아래에 설명되어 있습니다.
클라이언트-서버 지원을 구현하지 않은 서버에 오브젝트를 제출하는 시도는
405 Method Not Allowed 응답을 권장적으로 반환해야 합니다.
HTTP 캐싱 메커니즘 [RFC7234]은 적절한 경우 서버-클라이언트 양방향에서 최대한 준수해야 합니다.
클라이언트는 새 Activity에 대해 올바르게 주소지정할 책임이 있습니다.
이는 실제 클라이언트 구현에 어느 정도 의존하지만, 클라이언트는 서버가 to,
bto, cc, bcc, audience 필드에 있는 수신자에게만
Activity를 전달함을 인지해야 합니다.
Followers 컬렉션과/또는 Public 컬렉션은 새 Activity의 기본 주소지정 대상으로 적합한 선택입니다.
클라이언트는 새 Activity에 object, target, inReplyTo 및/또는
tag 필드를 통해 첨부된 오브젝트를 검사하고, 그 오브젝트의 actor 또는
attributedTo 속성(들)을 추출해야 하며, 선택적으로
주소지정 속성들도 가져와서, 새 Activity의 to나 cc에도 추가할 수 있습니다.
클라이언트는 첨부 오브젝트를 재귀적으로 따라가도 되지만, 이런 경우 권장적으로 제한을 두어야 합니다.
(참고: 이는 클라이언트가 주소지정되는 액터의 컬렉션 전체를 개별 수신자로 '펼치라'는 의미가 아님)
클라이언트는 이 주소지정 값을 UI에서 사용자가 수정할 기회를 제공해도 됩니다.
예를 들어 Chris가 Amy의 아래 게시글(article)에 좋아요를 누르는 경우:
{
"@context": ["https://www.w3.org/ns/activitystreams",
{"@language": "en-GB"}],
"id": "https://rhiaro.co.uk/2016/05/minimal-activitypub",
"type": "Article",
"name": "Minimal ActivityPub update client",
"content": "Today I finished morph, a client for posting ActivityStreams2...",
"attributedTo": "https://rhiaro.co.uk/#amy",
"to": "https://rhiaro.co.uk/followers/",
"cc": "https://e14n.com/evan"
}
클라이언트는 아래와 같이 like를 생성합니다:
{
"@context": ["https://www.w3.org/ns/activitystreams",
{"@language": "en"}],
"type": "Like",
"actor": "https://dustycloud.org/chris/",
"summary": "Chris liked 'Minimal ActivityPub update client'",
"object": "https://rhiaro.co.uk/2016/05/minimal-activitypub",
"to": ["https://rhiaro.co.uk/#amy",
"https://dustycloud.org/followers",
"https://rhiaro.co.uk/followers/"],
"cc": "https://e14n.com/evan"
}
받는 쪽 outbox는 delivery를 통해 Chris(좋아요 누른 사람)의 팔로워뿐 아니라, Amy와 Amy의 팔로워(Evan 포함)까지 알림을 보냅니다.
다음과 같은 Activity를 outbox에 제출하는 클라이언트는 object 속성을 반드시 Activity에 제공해야 합니다:
Create, Update, Delete,
Follow, Add, Remove,
Like, Block, Undo.
또한 다음 Activity를 outbox에 제출할 때는 target 속성도 반드시 제공해야
합니다:
Add, Remove.
Create Activity는 새 오브젝트 게시에 사용됩니다.
Activity 내부(object 속성)에 포함된 오브젝트가 생성된다는 부수 효과가 있습니다.
Create Activity가 게시될 때, 해당 Activity의 actor를 오브젝트의 attributedTo
필드에 반드시 복사해야 합니다.
Create Activity와 그 object의 주소지정이 다르면 혼란이 발생할 수 있으므로,
서버는 초기 배포 시 Create Activity의 수신자를 object에도 복사하고 반대도 권장적으로 복사해야 합니다.
단, 이후 object의 주소지정은 Create의 주소지정을 변경하지 않고 별도로 바뀔 수도 있습니다(예:
Update activity로).
클라이언트-서버 포스팅에서는, Activity로 감싸지 않은 오브젝트를 직접 제출하여 생성하는 것도 가능합니다.
서버는 Activity 서브타입이 아닌 유효한 [ActivityStreams] 오브젝트를 outbox에 반드시 수락해야 합니다.
이후 서버는 이 오브젝트를 Create Activity의 object로 반드시 감싸야 합니다.
일시적이지 않은(normal) 오브젝트의 경우 서버는 wrapping된 Create와 내부 Object 양쪽 모두에 id를 반드시 부여해야 합니다.
서버가 반환하는 Location 값은 오브젝트가 아닌 새 Create activity의 URL이어야 합니다.
오브젝트에 있는 to, bto, cc, bcc, audience
속성은 모두 반드시 서버가 새 Create Activity로 복사해야 합니다.
{
"@context": "https://www.w3.org/ns/activitystreams",
"type": "Note",
"content": "This is a note",
"published": "2015-02-10T15:04:55Z",
"to": ["https://example.org/~john/"],
"cc": ["https://example.com/~erik/followers",
"https://www.w3.org/ns/activitystreams#Public"]
}
{
"@context": "https://www.w3.org/ns/activitystreams",
"type": "Create",
"id": "https://example.net/~mallory/87374",
"actor": "https://example.net/~mallory",
"object": {
"id": "https://example.com/~mallory/note/72",
"type": "Note",
"attributedTo": "https://example.net/~mallory",
"content": "This is a note",
"published": "2015-02-10T15:04:55Z",
"to": ["https://example.org/~john/"],
"cc": ["https://example.com/~erik/followers",
"https://www.w3.org/ns/activitystreams#Public"]
},
"published": "2015-02-10T15:04:55Z",
"to": ["https://example.org/~john/"],
"cc": ["https://example.com/~erik/followers",
"https://www.w3.org/ns/activitystreams#Public"]
}
Update Activity는 기존 오브젝트를 수정할 때 사용됩니다.
이 Activity의 부수 효과로, 액터가 해당 오브젝트에 대한 수정 권한이 있는 경우 object가 Activity에서 정의한 새 구조로 반드시 변경됩니다.
클라이언트-서버 상호작용에서는 '부분 업데이트'가 지원됩니다.
문서 전체를 한 번에 수정하는 것이 아니라, 전달된 모든 키-값 쌍은 기존 값을 새 값으로 대체합니다.
이는 오브젝트 최상위 필드에만 적용됩니다.
특별 예외로, 값이 json의 null 타입이라면 해당 필드는 서버 오브젝트 표현에서 삭제되어야 합니다.
이 동작은 클라이언트가 서버에 POST하는 클라이언트-서버 상호작용에만 해당됩니다. 서버-서버 또는 서버→클라이언트 업데이트는 부분 업데이트가 적용된 이후의 전체 오브젝트 표현이 전달되어야 합니다. 자세한 내용은 서버-서버 상호작용을 위한 Update activity를 참고하세요.
Delete Activity는 기존 오브젝트를 삭제할 때 사용됩니다.
이 Activity의 부수 효과로, 서버는 object를 Tombstone 타입으로 대체해
삭제된 오브젝트를 참조하는 Activity에 표시될 수 있도록 동작할 수 있습니다.
삭제된 오브젝트 요청 시 서버는 Tombstone을 본문으로 반환한다면 410 Gone
상태코드로 답해야 하며, 그렇지 않으면 404 Not Found로 답해야 합니다.
삭제된 오브젝트 예시:
{
"@context": "https://www.w3.org/ns/activitystreams",
"id": "https://example.com/~alice/note/72",
"type": "Tombstone",
"published": "2015-02-10T15:04:55Z",
"updated": "2015-02-10T15:04:55Z",
"deleted": "2015-02-10T15:04:55Z"
}
Follow Activity는 다른 액터의 활동을 구독하는 데 사용됩니다.
outbox에서 이 Activity를 수신할 경우, 서버는 이후 Accept Activity가 이 Follow
Activity를 object로 포함해 도착할 때에만, object를 actor의 following 컬렉션에 추가해야 합니다.
outbox로 Add Activity를 수신 시, 서버는 target 속성의 컬렉션에
object를 추가해야 합니다. 단,
target이 수신 서버의 소유가 아니라서 서버가 갱신 권한이 없는 경우
object가 target 컬렉션에 추가되면 안 된다고 서버가 판단할 때
outbox로 Remove activity를 수신 시, 서버는 target의 컬렉션에서
object를 제거해야 합니다. 단,
target이 수신 서버 소유가 아니어서 갱신할 권한이 없는 경우
object를 target 컬렉션에서 제거하면 안 된다고 서버가 판단할 때
Like Activity는 actor가 object를 좋아함을 나타냅니다.
outbox에서 이 Activity를 수신 시, 서버는 object를 actor의 liked 컬렉션에 추가해야 합니다.
Block Activity는 게시 액터가 object 속성에 지정된 액터가 자신이 게시한 오브젝트와 상호작용하지 못하도록 하고 싶을 때
사용됩니다.
서버는 차단된 사용자가 액터가 게시한 오브젝트와 상호작용하지 못하도록 막아야 합니다.
서버는 Block Activity를 object에게 전달하면 안 됩니다.
Undo Activity는 이전 Activity를 취소할 때 사용됩니다.
자세한 내용은 Activity Vocabulary 문서의
역활동 및 "Undo" 항목을 참고하십시오.
예를 들어, Undo는 이전의 Like, Follow, Block을 되돌릴 때 활용할 수
있습니다.
undo 및 대상이 되는 활동은 반드시 같은 actor를 가져야 하며,
가능한 한 모든 부수 효과도 취소되어야 합니다.
(예: Like를 undo하면 이전에 올린 카운터도 적절히 감소해야 함)
단, 명시적인 역활동(inverse activity)이 따로 존재하는 경우에는 그것을 사용해야 합니다.
Create 기반 액티비티는 Delete로, Add는 Remove로 undo해야 합니다.
연합 서버는 outbox에 게시된 모든 Activity에 대해 outbox 전달을 반드시 수행해야 합니다.
이 섹션은 비규범적입니다.
서버는 이미지, 비디오 등 바이너리 데이터를 Activity에 참조될 수 있도록 사용하는 문서 업로드를 지원할 수 있지만, 자세한 메커니즘은 이번 ActivityPub 버전의 범위에 포함되어 있지 않습니다. 소셜웹 커뮤니티 그룹에서 이에 대한 상세 프로토콜을 ActivityPub Media Upload 보고서에서 논의 중입니다.
서버는 다른 서버와 통신하고 소셜 그래프 전반에 정보를 전파하기 위해 액터의
inbox 엔드포인트에 activity를 게시합니다.
네트워크를 통해 전송되는 Activity는 가능하면 id를 포함해야 하며,
일시적인 용도라면 생략해도 됩니다.
POST 요청(예: inbox로)은 반드시
Content-Type을 application/ld+json; profile="https://www.w3.org/ns/activitystreams"으로,
GET 요청은(참고 3.2 객체 검색)
Accept 헤더를 application/ld+json; profile="https://www.w3.org/ns/activitystreams"로 보내야 합니다.
서버는 권장적으로,
서버-서버 상호작용에서 application/activity+json의 Content-Type 또는 Accept 헤더를
application/ld+json; profile="https://www.w3.org/ns/activitystreams"와 동등하게 해석할 수 있습니다.
소셜 그래프 전체로 업데이트를 전파하기 위해, Activity는 적절한 수신자에게 전달되어야 합니다. 수신자는 오브젝트들 간의 링크를 따라 액터에 도달할 때까지 추적하여 결정되고, 그 후 Activity는 해당 액터의 inbox에 삽입됩니다 (전달). 이를 통해 수신 서버는 다음의 작업을 할 수 있습니다:
전달 동작은 예를 들어 다음 경우에 트리거됩니다:
다른 서버의 액터 inbox 또는
sharedInbox 속성에 Activity를 전달하는 서버는
아래 각 Activity별로 object 속성을 반드시 제공해야 합니다:
Create, Update, Delete,
Follow, Add, Remove,
Like, Block, Undo.
또한 아래 Activity에 대해서는 target 속성도 반드시 제공해야 합니다:
Add, Remove.
HTTP 캐싱 메커니즘 [RFC7234]은 적절한 경우 다른 서버로부터 응답을 받을 때뿐만 아니라, 응답을 보낼 때도 양쪽에서 권장적으로 준수해야 합니다.
아래 내용은 오직 연합(federated) 서버 간 통신에 필수입니다.
Activity는 우선 타겟(즉, 액터)의 inbox를 찾고 그 inbox에 Activity를 POST하여 전달됩니다.
전달 대상은
ActivityStreams audience targeting의 규정에 따라,
Activity의 to, bto, cc,
bcc, audience 필드를 확인해 결정합니다.
inbox는
먼저
대상 액터의 JSON-LD 표현을 조회한 뒤,
inbox 속성을 찾아 결정합니다.
수신자가 Collection 또는 OrderedCollection이라면,
서버는 반드시(사용자 인증 정보로) 컬렉션을 dereference해서,
컬렉션 내 각 항목의 inbox를 알아내야 합니다.
서버는 반드시 컬렉션을 통한 연쇄 dereference의 깊이에 제한을 두어야 하며, 보통 1단계만 허용할 수 있습니다.
서버는 최종 타겟 수신자 목록을 반드시 deduplicate(중복 제거)해야 합니다.
또한 Activity의 actor와 동일한 액터는 목록에서 반드시 제외합니다.
즉, 액터 본인이 본인의 Activity를 스스로 수신해서는 안 됩니다.
수신자가 명시되지 않은 경우의 처리는 정해져 있지 않지만, 명시 안 하면 해당 오브젝트가 완전히 비공개로 남고 접근 통제가 오브젝트 접근을 제한하는 것이 바람직합니다. 만약 단순히 "public" 컬렉션에만 전송하면, 어떤 액터에도 전달되지 않고 해당 액터의 outbox에서만 공개적으로 조회될 수 있습니다.
이후 inbox에(Submit 한 사용자 권한으로) HTTP POST 요청을 하며,
요청 본문에 Activity를 담습니다.
Activity는 수신자의 inbox OrderedCollection에 item으로 추가됩니다.
비연합 서버의 inbox로 전달을 시도한 경우 권장적으로
405 Method Not Allowed 응답을 받아야 합니다.
제3의 서버로 전달하는 연합 서버의 경우, 전달은 비동기로 수행해야 하며, 네트워크 오류로 인해 실패한다면 재시도 또한 수행해야 합니다.
참고: 동일 오리진 내 액터 간 Activity 유통에는 내부 메커니즘을 사용할 수 있으며 반드시 HTTP를 쓸 필요는 없습니다.
본 명세를 이해하기 위해 필수로 읽을 필요는 없으나,
ActivityPub의 타겟팅 및 전달 메커니즘은 Linked Data Notifications 명세와
상당 부분 겹치며,
양쪽을 함께 결합해서 동작시킬 수도 있음을 기억할 만 합니다.
특히 inbox 속성 자체는 ActivityPub과 Linked Data Notifications 모두 동일하며,
본 문서의 타겟팅 및 전달 시스템도 Linked Data Notifications에서 지원됩니다.
ActivityStreams 문서를 JSON-LD compacted 형태뿐만 아니라,
Linked Data Notifications는 여러 RDF 시리얼라이제이션도 지원하지만, 이는 ActivityPub 구현에 필수는 아닙니다.
단, LDN 구현과 더 광범위한 호환성을 원한다면 다른 RDF 표현도 지원할 수 있습니다.
outbox에 오브젝트가 수신될 때 (서버가 클라이언트-서버 상호작용과 서버-서버 상호작용을 모두 지원하는 경우), 서버는 아래 값이 개인 또는 액터 소유 컬렉션이면, 해당 필드로 전달 해야 합니다:
to, bto, cc,
bcc, audience
이 값들은 outbox에 Activity를 게시한 클라이언트가 적절히 지정합니다.
다음 섹션은 연합 네트워크에서 간혹 문제가 되는 "고스트 리플라이" 문제를 완화하기 위한 것입니다. 아래 예시에서 그 문제를 쉽게 볼 수 있습니다.
Alyssa가 학회 발표 성공 소식을 팔로워에게 알리면 Ben도 그 팔로워 목록에 있습니다. Ben은 Alyssa의 메시지에 축하 답글을 달고 본문에 그녀의 팔로워 컬렉션을 수신자로 포함합니다. 하지만 Ben은 Alyssa의 팔로워 컬렉션 멤버를 볼 권한이 없으니, Ben의 서버가 자신의 메시지를 그들의 inbox로 전달하지 못합니다. 이 전달 메커니즘이 없으면 Alyssa가 Ben에게 답장하면, Alyssa의 팔로워들은 Ben의 메시지를 받아본 적 없이 그의 이름이 언급된 답글만 보게 되고, 이는 혼란스럽게 됩니다.
Activity가 inbox에 수신될 때, 원본 서버가 전달하지 못한 수신자에게
다시 메시지를 포워딩해야 합니다. 이를 위해 서버는 아래 조건이 모두 true일 때만 to, cc,
audience 필드로
전달을 해야 합니다:
to, cc, audience 값 중 서버 소유 컬렉션이 포함된 경우
inReplyTo, object, target 및/또는 tag 값 중 서버가
소유한 오브젝트가 있는 경우.
서버는 가능하다면 이 값들을 재귀적으로 따라가며
서버가 소유한 연결 오브젝트를 탐색해야 하며, 재귀 깊이에 제한을 두어야 합니다.
단, 포워드할 때는 오리지널 오브젝트의 to, cc, audience만 참조하고,
재귀로 추가 탐색 중 새 수신자는 추가하지 않습니다(클라이언트에 의해 수신자가 변경/제한되었을 수 있으므로).
구현에 따라(예: 스팸 필터링 등) 전달 대상을 서버 임의 기준으로 필터할 수 있습니다.
inbox로 Create Activity를 수신해도 추가 부수 효과는 거의 없습니다;
Activity는 액터의 inbox에 표시되고, 서버는 이 Activity와 딸린 오브젝트의 내부 표현을 저장할 수 있습니다.
그러나 이는 원래 inbox로 전달되는 activity 처리 과정에서 항상 발생하는 현상입니다.
서버-서버 상호작용에서, Update Activity는 수신 서버가 동일 id를 가진 자체 object의 사본을
Activity에 첨부된 사본으로 업데이트해야 함을 뜻합니다.
클라이언트-서버 Update activity 처리와 달리,
전체 오브젝트를 완전히 덮어쓰는 전체 갱신 방식입니다.
수신 서버는 반드시 Update가 해당 object를
변경할 권한이 있는지 확인해야 합니다.
최소한, Update와 그 object가 같은 오리진인지를 점검해야 합니다.
이를 inbox로 수신하면(단, object가 송신 액터/서버 소유라는 전제 하에)
수신 서버는 동일 id의 object 내부 표현을 삭제
(및 Tombstone 오브젝트로 대체)해야 합니다.
(참고로, 오리진 서버에서 원격 서버로 activity를 전달한 뒤에는, ActivityPub 프로토콜에 원격 서버 오브젝트 표현의 삭제를 강제할 수 있는 장치는 없습니다.)
이를 inbox로 수신하면, 서버는 권장적으로
Follow를 object로 가진 Accept 또는 Reject activity를 생성해
해당 Follow의 actor에게 전달해야 합니다.
Accept 또는 Reject는 자동으로 생성할 수도 있고,
사용자 입력(예: 검토 후 응답 등) 결과로 생성할 수도 있습니다.
서버가 Reject를 명시적으로 보내지 않도록 할 수도 있지만,
이 경우 요청을 보낸 쪽 서버가 중간 상태에 남을 수 있음을 인지해야 합니다.
예를 들어, 사용자의 프라이버시 보호 위해 Reject를 보내지 않을 수도 있습니다.
Accept가 도착하고 그 object가 이전에 수신자가 보낸 Follow Activity일 경우,
서버는 권장적으로 actor를 수신자의 Followers
Collection에 추가해야 합니다.
Reject의 경우, 서버는 절대 actor를 수신자의 Followers Collection에 추가하면 안 됩니다.
때때로 Follow 구독이 성공했어도, 나중에 배달이 장기간 실패할 수 있습니다.
네트워크 내 액터가 계속 접근 가능할 것이라는 보장은 없으므로, 구현에서는 이런 상황을 반드시 고려해야 합니다.
예를 들어 팔로워가 6개월 동안 접근 불가 상태면, 배달 서버가 해당 구독자를 followers 목록에서 제거하는 것이 합리적일 수 있습니다.
시간 기준 및 행동 방식은 배달 서버의 재량입니다.
이를 inbox에서 수신할 때의 부수 효과는 수신한 object 타입에 따라 다르며,
문서에 정의되지 않은 타입(예: Offer 등)도 수락할 수 있습니다.
inbox로 도착한 Accept의 object가 이전에 수신자가 보낸 Follow
activity라면,
서버는 권장적으로 actor를 수신자의 Following
Collection에 추가해야 합니다.
이를 inbox에서 수신할 때의 부수 효과는 수신한 object 타입에 따라 다르며,
문서에 정의되지 않은 타입(예: Offer 등)도 거절할 수 있습니다.
inbox로 수신된 Reject의 object가 수신자가 이전에 보낸 Follow
activity라면,
이는 수신자가 Follow 요청을 승인하지 않았음을 의미합니다.
서버는 절대 actor를 수신자의 Following
Collection에 추가하면 안 됩니다.
inbox로 Add Activity를 수신하면,
서버는 target 속성의 컬렉션에 object를 추가해야 합니다
(단,
target이 수신 서버 소유가 아니어서 갱신할 권한이 없는 경우
object가 target 컬렉션에 추가되면 안 되는 경우
inbox로 Remove Activity를 수신하면,
서버는 target 속성의 컬렉션에서 object를 제거해야 합니다
(단,
target이 수신 서버 소유가 아니어서 갱신할 권한이 없는 경우
object가 target 컬렉션에서 제거되면 안 되는 경우
inbox로 이 Activity를 수신하면,
서버는 해당 오브젝트의 like 개수를 증가시키고, 받은 Activity를
likes 컬렉션에(존재한다면) 추가해야 합니다.
inbox로 Announce Activity를 수신하면,
서버는 해당 오브젝트의 공유 개수를 증가시키고, 받은 Activity를
shares 컬렉션에(존재한다면) 추가해야 합니다.
Announce Activity는 타 소셜 네트워크에서 "공유", "리포스트", "부스트"라고 알려진 기능과 동일합니다.
Undo Activity는 이전 Activity의 부수 효과를 되돌릴 때 사용합니다.
ActivityStreams 문서의
역활동 및 "Undo"를 참고하세요.
Undo Activity의 범위와 제한은
클라이언트-서버 상호작용 맥락의 Undo activity와 동일하며,
그 적용 대상이 연합 환경임을 제외하고 동일합니다.
이 섹션은 비규범적입니다.
연합 네트워크에서는 다양한 국가의 사용자를 확보하는 것이 중요합니다.
ActivityStreams는 콘텐츠의 국제화를 위한 도구를 제공합니다.
이는 가능한 한 항상 사용해야 합니다.
그러나 구현체가 사용자가 입력한 콘텐츠에
@language 속성
을 어떻게 지정할지 결정하는 것은 어려울 수 있습니다.
W3C
국제화 그룹에서는
언어 감지에 대한 가이드를 제공합니다.
이 섹션은 비규범적입니다.
서버는 클라이언트가 제출한 콘텐츠를 신뢰해서는 안 되며, 연합 서버 또한 오리진이 아닌 다른 서버로부터 받은 콘텐츠는 어느 정도의 검증 절차 없이는 신뢰하지 않아야 합니다.
서버는 신규 콘텐츠가 실제로 이를 게시한다 주장하는 액터에 의해 작성된 것인지, 그리고 액터가 자신이 변경하려는 리소스를 변경할 권한이 있는지 반드시 확인해야 합니다. 또한 3. 객체(Objects)와 B.1 인증 및 권한 부여도 참고하세요.
개발 시에는 localhost에서 동작하는 프로세스를 대상으로 테스트하는 것이 편리한 경우가 많습니다. 하지만 실제 서비스에서 클라이언트나 서버 인스턴스가 localhost로의 요청을 허용하는 것은 위험할 수 있습니다. 인증이 필요 없는 localhost URI로 요청을 보낼 경우, 로컬에서만 접근 가능하다고 가정된 리소스에 의도치 않게 접근하거나 변경될 수 있습니다.
ActivityPub 서버나 클라이언트가 개발 목적으로 localhost URI에 요청을 허용한다면, 반드시 설정 옵션화하여 기본값을 '꺼짐'으로 두는 것이 좋습니다.
http, https 외에도 다양한 종류의 URI가 존재합니다.
일부 라이브러리는 여러 URI 스킴의 요청을 똑똑하게 처리하려 하지만,
file 등 원치 않는 스킴이 포함되어 있을 수 있습니다.
클라이언트/서버 작성자는 자신의 라이브러리가 어떻게 요청을 처리하는지 꼼꼼히 확인하고,
http와 https처럼 안전한 유형만 화이트리스트할 것을 권고합니다.
서버는 오브젝트를 재귀적으로 해석할 때 최대 깊이 제한을 두거나, 재귀 참조가 걸린 ActivityStreams 오브젝트를 특별히 처리해야 합니다. 이를 소홀히 하면 서비스 거부(DoS) 취약점이 발생할 수 있습니다.
네트워크라면 어디든 스팸이 문제이고, 연합 네트워크에서는 특히 더 심각할 수 있습니다. ActivityPub은 스팸 방지를 위한 구체적 메커니즘을 제공하지 않으나, 서버가 신뢰되지 않은 로컬 사용자뿐만 아니라 원격 사용자에 대해서도 스팸 필터 등으로 수신 콘텐츠를 필터링하는 것이 권고됩니다.
서버는 연합 서버로부터의 서비스 거부(DoS) 공격에 대한 방어책을 구현해야 합니다. 예를 들어, 어떤 형태로든 레이트리밋(ratelimiting) 메커니즘을 쓸 수 있습니다. 특히 부수 효과를 수반하는 activity 주변에는 이런 방어책을 특히 강화해야 합니다. 서버는 권장적으로, 예를 들어 지수적 백오프(exponential backoff) 전략을 써서 제출로 인해 다른 서버가 과부하되지 않도록 해야 합니다.
서버는 API 클라이언트 제출을 레이트리밋해야 합니다. 그 이유는 두 가지입니다:
클라이언트가 너무 큰 컬렉션 때문에 과부하되는 상황을 막기 위해, 서버는 반환하는 컬렉션 페이지의 크기를 제한해야 합니다. 클라이언트도 악의적이거나 손상된 서버에 연결될 경우를 대비해 처리할 최대 응답 크기를 별도로 제한(예: 타임아웃 및 에러 발생)해야 합니다.
브라우저 등(또는 기타 리치 텍스트 앱)에 표시되는 activity 필드는, 크로스사이트 스크립팅 공격을 막기 위해 마크업이 포함된 필드를 반드시 정제(sanitize)해야 합니다.
bto와 bcc는
전달 전에 반드시 제거되어야 하며,
서버는 오브젝트를 자체 스토리지 시스템에서 어떻게 표현할지는 자유입니다.
그러나 bto, bcc는 오브젝트/액티비티의 원래 작성자만 알아야 하므로,
화면 표시 시에도 해당 속성은 생략해야 합니다.
이 섹션은 비규범적입니다.
이 명세는 웹에서 연합이라는 분야를 개척해 온 여러 커뮤니티의 오랜 노고와 경험에서 비롯됐습니다. 특히 본 명세의 많은 부분은 OStatus 그리고 Pump API (StatusNet(현 GNU Social) 및 Pump.io가 선구적으로 개발) 의 영향이 큽니다. 이 두 프로젝트 모두 개발자들의 헌신적 노고의 산물이지만, 그 중에서도 Evan Prodromou는 이 분야의 한결같은 리더였으며, 그의 노력이 없었다면 ActivityPub은 지금과 같은 모습으로 존재하지 못했을 것입니다.
Erin Shepherd는 본 명세의 초안을 작성했으며, Pump API 문서의 아이디어에서 출발해 ActivityStreams 1에서 ActivityStreams 2로 바뀐 대목은 거의 모두 새로 썼지만, 주요 개념 상당수는 유지하는 완전 재작성에 가까웠습니다.
표준이 W3C Social Working Group으로 이관된 이후, Jessica Tallon과 Christine Lemmer-Webber가 편집을 맡아 Erin Shepherd의 문서에서 현재 ActivityPub 문서로 전환하는 작업을 주도했습니다. 문서의 많은 부분이 Social Working Group의 오랜 피드백을 거치며 새로 쓰이고 재구성됐습니다.
ActivityPub은 W3C Social Working Group의 많은 멤버들이 꼼꼼하게 남긴 의견을 통해 발전했습니다. 특히 Amy Guy에게 큰 빚을 졌습니다. Amy는 [Social-Web-Protocols] 작업을 통해 여러 Social Working Group 문서 전반의 연결을 누구보다 일목요연하게 정리했고, Christopher Allan Webber와 나흘간의 스프린트로 ActivityPub 명세의 대규모 구조조정의 토대를 닦아주기도 했습니다. 이 개정은 클라이언트-서버/서버 컴포넌트의 구분을 더욱 명확히 하고, ActivityPub 과 [LDN]과의 관계 설명에 명확성을 더하는 등 큰 발전에 기여했습니다. Benjamin Goering이 구현 보고서 템플릿을 만들어 준 데도 특별히 감사드립니다. 명세 전반의 멋진 튜토리얼 일러스트(문서와 동일한 라이선스임)를 제작해 준 mray에게도 감사를 전합니다.
많은 분의 꼼꼼한 리뷰와 피드백도 ActivityPub 발전에 큰 힘이 됐습니다. 특히 다음 분들께 감사드립니다: Aaron Parecki, AJ Jordan, Benjamin Goering, Caleb Langeslag, Elsa Balderrama, elf Pavlik, Eugen Rochko, Erik Wilde, Jason Robinson, Manu Sporny, Michael Vogel, Mike Macgirvin, nightpool, Puck Meerburg, Sandro Hawke, Sarven Capadisli, Tantek Çelik, 그리고 Yuri Volkov.
이 문서는 지구상의 모든 시민에게 바칩니다. 당신은 소통의 자유를 누릴 자격이 있습니다. 우리는 미약하나마 그 권리와 목표에 기여하고자 이 문서를 준비했습니다.
1.1 소셜 웹 워킹 그룹
ActivityPub은 소셜 웹 워킹 그룹에서 제작 중인 여러 관련 명세 중 하나입니다. 다른 방법이나 보완 프로토콜에 관심 있는 구현자는 [Micropub] 및 개요 문서 [Social-Web-Protocols]를 참고하세요.