FEP-7888: Demystifying the context property
Summary
ActivityStreams Vocabulary defines the context
property, but it is "intentionally vague". Unfortunately, this makes the definition so vague as to be practically useless. This FEP aims to provide more guidance on possible uses of the context
property, as well as formalizing some best practices.
Overview
(このセクションは非規範的です。)
See "Appendix A: Rationale" for fuller analysis of the definition, as well as use cases that can be mapped onto context
.
In short:
- It is possible for objects to exist within implicit contexts. For example, you might group all objects sharing a certain
tag
, or all objects having a commonaudience
. - An explicit
context
embodies purpose. Things grouped by the samecontext
"belong together" in a way that can't be said about things grouped by the sametag
. - Objects with a
context
exist within that context, and are meant to be seen and interpreted in context of something else. In most cases, you do not want to view the object on its own; it should be viewed together with other objects, contextually. - You might use
context
to represent a "thread", "topic", "conversation", "room", "channel", "forum", "wall", "guild", "space", "project", or so on.
The requirements below can be summarized like so:
- Publishers can use
context
for signaling which objects belong together purposefully, i.e. objects that are meant to be viewed or processed together. Ideally, make thecontext
resolve to something useful. Depending on which properties the resolved context has, various use cases can make use of those properties. - For example, if the context is
attributedTo
some owner, others can keep that owner in the loop when interacting with their context. Similar considerations apply tofollowers
andaudience
. - If the context has some canonical collection associated with it that represents the contents of that context, then that collection can be used for backfill, authorization, moderation, synchronization, and so on.
- Consumers can use
context
to group related objects by the context'sid
. The graph source for those objects is up to you, but in the case where your source is an SQL database, it probably makes sense to have thecontext.id
be the value of a column which is indexed so you can efficiently use a WHERE clause in your query. - Declaring a context does not imply that the context owner acknowledges that object. A canonical collection can help with verifying this for the purpose of authorization or moderation.
- Interactions with an object that has a
context
might exist within that same context, or they might declare their own context, or they might not declare a context. If you're declaring someone else's context, then you might want to keep them in the loop, in the same way that you'd keep someone in the loop if you replied to their object or tagged their object. This would be signaled viacontext.attributedTo
similarly toinReplyTo.attributedTo
ortag[*].attributedTo
.
Publishing context
When generating an object with a context
as a publisher:
Purpose
A context
SHOULD have a purpose; consider tag
for looser references. Objects sharing a certain context
SHOULD be strongly related and intended to be viewed in the same grouping.
Dereferencing and resolving
A context
SHOULD be resolvable. The resolved object or link can describe the context with at least the additional information needed to fully process the activity or object. Examples of generally useful properties include but are not limited to:
attributedTo
denotes the authority for that context. (This authority might be a good target for addressing and delivery of related activities.)audience
indicates intent for or potential interest by some entities. (These entities might be a good target for addressing and delivery of related activities.)followers
signals that the context might be a followable object. (This followers collection might be a good target for addressing and delivery of related activities.)outbox
might contain relevant Activities performed by the context, if it is an actor.
Ideally, the resolved context
SHOULD in some way have an associated Collection which can contain the related items. (The exact semantics of discovering this Collection are out of scope of this FEP.)
Consuming context
When encountering an object with a context
as a consumer or browser:
Group objects by context
At minimum, you SHOULD consider the current object alongside other objects referencing the same context
(by id
) instead of considering the current object independently. By default, the graph source for objects that are being considered for inclusion is arbitrary. This can be some dataset, or it can be some relevant collection's items. For example, you might conssider the outbox and/or inbox of one or more actors, or you might consider a specific property path on the context
(if resolvable).
Canonical collections of objects within an authoritative context
If the context
resolves to an object of a certain type, then that type MAY indicate that a certain relation represents a canonical Collection of all objects that the authority considers to be included. (The definition of such types and relations is out of scope of this FEP.)
For authoritative contexts that include such a canonical Collection, you SHOULD NOT assume that an object has been accepted into that collection simply because it declares context
. Consumers SHOULD make efforts to verify reverse claims of inclusion. If a client or user-agent is unable to verify this claim, then the client or user-agent SHOULD indicate to users that the object's claim of being included in the authoritative context is unverified. Criteria for establishing proof of inclusion in a collection is out of scope for this FEP, but might include:
- Viewing the collection directly and encountering the object as a collection item
- Querying the collection via some querying mechanism that allows determining if a given object is included in a collection.
- Having knowledge that the authority Added the object to the collection, with knowledge that the object wasn't subsequently Removed.
Interacting with context
Choosing whether to participate in the same context
When encountering an object with a context
and choosing to author your own object or activity that interacts with this object:
- You MAY copy a
context
as-is, if you wish for your object to be included in that same context. - You MAY set your own
context
, if you wish for your object to exist in a different context. - You MAY remove the
context
entirely, if you wish for your object to exist on its own.
Note that context
can be present on either the object, the activity, or both. It is also possible for different context
references to be placed on each. This depends on how context
is used. In the case of an activity wrapping an object with context, if the activity is deemed to exist in the same context as the object, then you SHOULD use the same context
reference on both the object and also on its wrapping activity.
Keeping relevant entities in the loop
Per PUB Section 6.1 "Client Addressing":
Clients SHOULD look at any objects attached to the new Activity via the
object
,target
,inReplyTo
and/ortag
fields, retrieve their actor or attributedTo properties, and MAY also retrieve their addressing properties, and add these to theto
orcc
fields of the new Activity being created. Clients MAY recurse through attached objects, but if doing so, SHOULD set a limit for this recursion. (Note that this does not suggest that the client should "unpack" collections of actors being addressed as individual recipients).Clients MAY give the user the chance to amend this addressing in the UI.
This FEP extends the recommendation to look at object
, target
, inReplyTo
, and/or tag
to also include context
.
If copying someone else's context, you SHOULD send your activity to the owner(s) of the context(s), defined via context.attributedTo
if resolvable. This is similar to how one might address the author of an object that they are responding to via inReplyTo.attributedTo
, as a social courtesy. You MAY also want to address context.followers
and/or addressing properties like context.audience
.
Appendix A: Rationale
(このセクションは非規範的です。)
The existing definition
From the current definition in VOCAB: https://www.w3.org/TR/activitystreams-vocabulary/#dfn-context
Identifies the context within which the object exists or an activity was performed.
The notion of "context" used is intentionally vague. The intended function is to serve as a means of grouping objects and activities that share a common originating context or purpose. An example could be all activities relating to a common project or event.
Aside from being "intentionally vague", the definition is also somewhat circular; it requires knowing what a context is and having some conceptual understanding of the notion of "context". However, we are given some guidance towards its "intended function", which is to group objects by some common purpose or origin.
Purpose and intent; or, why not use a tag?
We might similarly use a tag
for grouping objects and activities. Several fediverse projects often include a Hashtag
(defined as an extension within the ActivityStreams namespace, but not actually adopted or defined formally). This Hashtag
signals an intent to be included or discovered through a collection of objects bearing the same Hashtag
, uniquely identified by its name
. The maintenance of such implicit collections is assumed to be the responsibility of the receiving server, although an href
might be provided for convenience, in order to browse the implicit collection of tagged objects as seen from that origin server. (This also makes the Hashtag
a sub-type of Link
.)
The key property of such a tag is to signal a general, implicit association by reference. We might then consider a context to be an explicit association, but such an explicit association requires an explicit definition.
The different types of context, and how they are actually the same
Various dictionaries define context generally as something that helps you understand the situation. Following from this, the context should be something that helps you process the activity or object. Ignoring the context may lead to misunderstanding the activity or object; the object or activity exists within that context, and should be understood in context of that context.
Specific contexts can be thought of in several applications:
- the "authoritative context" is a context in which some authority can be applied;
- the "conversational context" is a context which represents some conversation and possibly its history;
- the "originating context" is a context which represents some intended starting point that you might look at first.
We might continue to articulate further types of contexts, but the general pattern that emerges is that a context exists to form a purposeful grouping, regardless of the specific purpose. For example, if we had the notion of a conversation, then we might reasonably say that someone owns this conversation and can apply their authority to it. Looking at some object or activity within this context is generally not recommended on its own; it is better to view the entire conversation or some page of it rather than viewing a singular object.
Sample workflows and use-cases involving context
The context may be presented using the following abstractions:
- A "topic" in a forum presentation
- A "conversation" in a social networking presentation
- A "room" in a chatting or messaging presentation
- A "thread" in any of the above contexts (forum thread, social media thread, chat thread)
Contexts may be associated with other contexts:
- A forum topic/thread may be nested in a "forum" or "forum category", and may be nested in another parent forum as a sub-forum.
- A "wall" on a social networking profile may contain conversations, which in turn contain the posts/comments
- A "guild" or "space" may contain multiple chat rooms with a common audience
It is also possible to not have a context. Such objects exist only in the general context of their author (via attributedTo
) or other implicit contexts, and are otherwise self-sufficient. This can include:
- An article published on a web site, particularly one meant to be accessible directly via a permalink
- A post in a blogging or microblogging environment, particularly one that does not represent a conversation, or where
inReplyTo
is meant only as a loose reference. - An activity intended for or acting upon an object without a context
Appendix B: Examples
(このセクションは非規範的です。)
Example 1: A minimal example for participating in a context
You encounter the following object:
{
"@context": "https://www.w3.org/ns/activitystreams",
"id": "https://domain.example/some-object",
"context": "https://domain.example/some-context",
"content": "Hello world",
"summary": "<some-object> exists in <some-context> and has content saying \"Hello world\"."
}
You wish to participate in the context, so you dereference the context:
{
"@context": "https://www.w3.org/ns/activitystreams",
"id": "https://domain.example/some-context",
"attributedTo": "https://domain.example/context-owner",
"audience": "https://domain.example/context-audience",
"type": "https://extension.example/SomeType",
"https://extension.example/someProperty": {
"id": "https://domain.example/some-context/collection",
"type": "OrderedCollection",
"orderedItems": ["https://domain.example/some-object"],
},
"summary": "<some-context> is a context owned by <context-owner> and with an audience of <context-audience>. It has a canonical collection <some-context/collection> which currently contains <some-object>."
}
You send your activity to the context authority, while copying their context:
{
"@context": "https://www.w3.org/ns/activitystreams",
"id": "https://domain.example/your-activity",
"actor": "https://domain.example/you",
"type": "Create",
"object": {
"id": "https://domain.example/your-object",
"context": "https://domain.example/some-context",
"content": "Hello!"
},
"audience": ["https://domain.example/context-owner", "https://domain.example/context-audience"],
"summary": "<you> Created <your-object> in <some-context>, and are delivering this activity addressing <context-owner> and <context-audience>."
}
The context authority then might forward this activity to the context audience.
The context authority also might add your object or message to the context's canonical collection:
{
"@context": "https://www.w3.org/ns/activitystreams",
"id": "https://domain.example/their-activity",
"actor": "https://domain.example/context-owner",
"type": "Add",
"object": "https://domain.example/your-object",
"target": "https://domain.example/some-context/collection",
"audience": [
"https://domain.example/context-owner-followers",
"https://domain.example/context-audience",
"https://domain.example/you", // to keep you in the loop
"https://domain.example/your-followers" // for inbox forwarding
],
"summary": "The <context-owner> Added <your-object> to <some-context/collection>, and they are delivering this activity addressing their followers, the <context-audience>, <you>, and <your-followers>."
}
The resulting state of the context's canonical collection is now like so:
{
"@context": "https://www.w3.org/ns/activitystreams",
"id": "https://domain.example/some-context",
"attributedTo": "https://domain.example/context-owner",
"audience": "https://domain.example/context-audience",
"type": "https://extension.example/SomeType",
"https://extension.example/someProperty": {
"id": "https://domain.example/some-context/collection",
"type": "OrderedCollection",
"orderedItems": [
"https://domain.example/some-object",
"https://domain.example/your-object"
],
},
"summary": "<some-context/collection> now contains <some-object> and <your-object> after the latter has been Added to the OrderedCollection by <context-owner>."
}
Third-party observers (such as members of https://domain.example/context-audience
) can validate or identify the Add activity as modifying a canonical collection by using the following checks:
Add.actor
==target.attributedTo
(the actor is allowed to modify a collection that they own)target
==object.context
(the object is being added into its declared context)
Alternatively, observers can use the Add as a signal to invalidate their cache of the canonical collection and refetch it.
Example 2: Choosing not to participate in the same context
You encounter the following object:
{
"@context": "https://www.w3.org/ns/activitystreams",
"id": "https://domain.example/some-object",
"context": "https://domain.example/some-context",
"content": "Hello world",
"summary": "<some-object> exists in <some-context> and has content saying \"Hello world\"."
}
You want to establish your own context, separately from the current object's context, so you create a canonical collection to be referenced from a context object, then create that context object. This results in two activities:
{
"@context": "https://www.w3.org/ns/activitystreams",
"id": "https://domain.example/creating-a-context-collection",
"actor": "https://domain.example/you",
"type": "Create",
"object": {
"id": "https://domain.example/a-different-context/collection",
"type": "OrderedCollection",
"attributedTo": "https://domain.example/you"
},
"summary": "<you> Created <a-different-context/collection>, which is an OrderedCollection."
}
{
"@context": "https://www.w3.org/ns/activitystreams",
"id": "https://domain.example/creating-a-different-context",
"actor": "https://domain.example/you",
"type": "Create",
"object": {
"id": "https://domain.example/a-different-context",
"type": "https://extension.example/SomeType",
"https://extension.example/someProperty": "https://domain.example/a-different-context/collection",
"attributedTo": "https://domain.example/you"
},
"summary": "<you> Created <a-different-context>, which is SomeType and has an associated OrderedCollection via someProperty."
}
You can now set this new context on your interaction, with the intent that your interaction is shown in a separate grouping from the original object:
{
"@context": "https://www.w3.org/ns/activitystreams",
"id": "https://domain.example/creating-a-post-in-a-different-context",
"actor": "https://domain.example/you",
"type": "Create",
"object": {
"id": "https://domain.example/a-post-in-a-different-context",
"content": "Starting a new thread to say \"Hello World\" in a different context.",
"inReplyTo": {
"id": "https://domain.example/some-object",
"context": "https://domain.example/some-context",
"content": "Hello world",
"summary": "<some-object> exists in <some-context> and has content saying \"Hello world\"."
},
"context": "https://domain.example/a-different-context"
},
"summary": "<you> Created <a-post-in-a-different-context> which is inReplyTo <some-object> in <some-context>, but your object is in <a-different-context>."
}
You add your object to your own context's canonical collection:
{
"@context": "https://www.w3.org/ns/activitystreams",
"id": "https://domain.example/their-activity",
"actor": "https://domain.example/you",
"type": "Add",
"object": "https://domain.example/a-post-in-a-different-context",
"target": "https://domain.example/a-different-context/collection",
"summary": "<you> Added <a-post-in-a-different-context> to <a-different-context/collection>."
}
The original object's context is unchanged:
{
"@context": "https://www.w3.org/ns/activitystreams",
"id": "https://domain.example/some-context",
"attributedTo": "https://domain.example/context-owner",
"audience": "https://domain.example/context-audience",
"type": "https://extension.example/SomeType",
"https://extension.example/someProperty": {
"id": "https://domain.example/some-context/collection",
"type": "OrderedCollection",
"orderedItems": ["https://domain.example/some-object"],
},
"summary": "<some-context> is a context owned by <context-owner> and with an audience of <context-audience>. It has a canonical collection <some-context/collection> which currently contains <some-object>."
}
Your context now looks like this:
{
"@context": "https://www.w3.org/ns/activitystreams",
"id": "https://domain.example/a-different-context",
"attributedTo": "https://domain.example/you",
"audience": "https://domain.example/some-audience",
"type": "https://extension.example/SomeType",
"https://extension.example/someProperty": {
"id": "https://domain.example/a-different-context/collection",
"type": "OrderedCollection",
"orderedItems": ["https://domain.example/a-post-in-a-different-context"],
},
"summary": "<a-different-context> is a context owned by <you> and with an audience of <some-audience>. It has a canonical collection <a-different-context/collection> which currently contains <a-post-in-a-different-context>."
}
Example 3: Encountering multiple contexts
{
"@context": "https://www.w3.org/ns/activitystreams",
"id": "https://domain.example/some-object",
"context": ["https://domain.example/some-context", "https://domain.example/some-other-context"]
"content": "Hello world",
"summary": "<some-object> exists in <some-context> and <some-other-context> and has content saying \"Hello world\"."
}
You dereference the two contexts:
{
"@context": "https://www.w3.org/ns/activitystreams",
"id": "https://domain.example/some-context",
"attributedTo": "https://domain.example/context-owner",
"type": "Object",
"summary": "<some-context> is an Object owned by <context-owner>.",
"https://extension.example/someProperty": "https://domain.example/some-context/collection"
}
{
"@context": "https://www.w3.org/ns/activitystreams",
"id": "https://domain.example/some-other-context",
"attributedTo": "https://domain.example/other-context-owner",
"type": "Object",
"summary": "<some-other-context> is an Object owned by <other-context-owner>.",
"https://extension.example/someProperty": "https://domain.example/some-other-context/collection"
}
You can now choose to participate in either context, both contexts, a different context, or no context.
To participate in both contexts:
{
"@context": "https://www.w3.org/ns/activitystreams",
"id": "https://domain.example/your-activity",
"actor": "https://domain.example/you",
"type": "Create",
"object": {
"id": "https://domain.example/your-object",
"context": ["https://domain.example/some-context", "https://domain.example/some-other-context"],
"content": "Hello!"
},
"audience": ["https://domain.example/context-owner", "https://domain.example/other-context-owner"],
"summary": "<you> Created <your-object> in <some-context> and <some-other-context>, and are delivering this activity addressing <context-owner> and <other-context-owner>."
}
It may be that you are refused addition into some context, but granted addition into some other context:
{
"@context": "https://www.w3.org/ns/activitystreams",
"id": "https://domain.example/context-1-activity",
"actor": "https://domain.example/context-owner",
"type": "Reject",
"object": "https://domain.example/your-activity",
"summary": "<context-owner> Rejected <your-activity>.",
"content": "Sorry, I don't want to add your object to my context."
}
{
"@context": "https://www.w3.org/ns/activitystreams",
"id": "https://domain.example/context-2-activity",
"actor": "https://domain.example/other-context-owner",
"type": "Add",
"object": "https://domain.example/your-object",
"target": "https://domain.example/some-other-context/collection",
"summary": "<other-context-owner> Added <your-object> to <some-other-context>."
}
A third-party observer encounters your object:
{
"id": "https://domain.example/your-object",
"context": ["https://domain.example/some-context", "https://domain.example/some-other-context"],
"content": "Hello!"
}
They attempt to verify that your object is included in the first context, and find that it is not included:
{
"@context": "https://www.w3.org/ns/activitystreams",
"id": "https://domain.example/some-context/collection",
"attributedTo": "https://domain.example/other-context-owner",
"type": "OrderedCollection",
"orderedItems": ["https://domain.example/some-object", "https://domain.example/your-object"],
"summary": "<some-other-context/collection> is an OrderedCollection owned by <other-context-owner>. It currently contains <some-object> and <your-object>."
}
The third-party observer attempts to verify that your object is included in the second context, and finds that it is included:
{
"@context": "https://www.w3.org/ns/activitystreams",
"id": "https://domain.example/some-other-context/collection",
"attributedTo": "https://domain.example/other-context-owner",
"type": "OrderedCollection",
"orderedItems": ["https://domain.example/some-other-object", "https://domain.example/your-object"],
"summary": "<some-other-context/collection> is an OrderedCollection owned by <other-context-owner>. It currently contains <some-other-object> and <your-object>."
}
The third-party observer's client renders your object as unverifiably within some context, and verifiably within some other context.
<you> Created <your-object>
<your-object> has content: "Hello!"
<your-object> is part of the following contexts:
- <some-context> (❓ unverified)
- <some-other-context> (✅ verified)
The third-party observer may then choose to navigate to (and possibly participate in) the latter context.
Appendix C: Creating and maintaining contexts and their associated collections using ActivityPub C2S
(このセクションは非規範的です。)
Because PUB does not define the use of context
as a property or the notion of a canonical collection, it is up to ActivityPub Clients to manage contexts and their canonical collections for themselves. The following algorithm may be used to create an object within a context that has a canonical collection:
Create
the canonicalCollection
that will be associated with the context. Save the generated Collectionid
to be used in the next step.- Create the Object that will be used as
context
. If the Object has a canonical Collection associated with it, then specify the appropriate property relation using theid
from the previous step. Save the generated contextid
to be used in the next step. - Create the Object that will exist within the context, and specify the
context
as theid
from the previous step. Set an appropriateaudience
or useto
/cc
to deliver the Create activity as-is. Save the generated objectid
to be used in the next step. - Add the Object to the context's canonical Collection, using the
id
s obtained from the responses for steps 1 and 3. You may wish to deliver this Add activity viato
/cc
/audience
targeting your intended recipients, especially if you did not deliver the Create Object from step 3.
参考文献
- VOCAB James M Snell, Evan Prodromou, Activity Vocabulary, 2017
- PUB Christine Lemmer Webber, Jessica Tallon, ActivityPub, 2018
著作権
CC0 1.0 ユニバーサル (CC0 1.0) パブリック ドメイン
法律で認められる範囲において、この Fediverse 拡張提案の著者は、この作品に対するすべての著作権および関連する権利または隣接する権利を放棄しています。