fep

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

(This section is non-normative.)

See “Appendix A: Rationale” for fuller analysis of the definition, as well as use cases that can be mapped onto context.

In short:

The requirements below can be summarized like so:

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. Deleting the context MAY delete all objects within that context.

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:

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:

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:

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 within a given protocol. Protocol considerations for when to use certain contexts are out-of-scope for this FEP. Protocol considerations for how to negotiate participation in someone else’s context are also out-of-scope for this FEP.

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/or tag fields, retrieve their actor or attributedTo properties, and MAY also retrieve their addressing properties, and add these to the to or cc 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

(This section is non-normative.)

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.

Supporting statements from spec authors

In a GitHub issue from ActivityStreams 2.0’s development cycle, James Snell provides the following example:

{
  "type": "Note",
  "content": "This is a note",
  "scope": {
    "type": "Organization",
    "name": "My Employer"
  },
  "to": ["john@example.com", "sally@example.com"],
  "context": {
    "type": "http://example.org/types/Project",
    "name": "A Project"
  }
}

James Snell then comments that:

James Snell then clarifies (emphasis added):

scope is not access control […] a consuming implementation may include the note on the activity timeline of anyone associated with the “My Employer” organization, but it would only notify two individuals listed by the to property. The context property, on the other hand, has absolutely nothing to do with audience targeting. The above note is essentially saying, “This is a note that was created in relation to A Project. Make the note available to anyone in the My Employer organization but specifically notify John and Sally”

Therefore, we can establish that context as a property roughly translates to a label of “was created in relation to”.

In a separate issue, James Snell provides another explanation:

The context is really intended to allow objects and activities to be logically grouped. For instance, in an enterprise setting, the context may group activities by project while the scope would identify one or more teams for which the activity is considered relevant, while the to/cc fields are used to indicate specific individuals to notify.

scope was later renamed to audience, but the two properties remain closely related and are presented together in AS2-VOCAB Section 5.1.1 “Audience and Context”:

Activities are rarely isolated events. Often, multiple individual activities will be performed around a similar context or audience. For instance, a collaborators working on a shared project might perform multiple related activities in the process of achieving some goal. Such activities can be logically grouped together using the context property, and scoped to a particular audience using the audience property.

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:

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:

Contexts may be associated with other contexts:

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.

Considerations on when to use context include:


Appendix B: Examples

(This section is non-normative.)

Example 1: A minimal example for grouping objects by context

This example demonstrates how objects sharing the same context can be logically grouped together.

You encounter the following object:

{
  "@context": "https://www.w3.org/ns/activitystreams",
  "id": "https://domain.example/some-object",
  "context": "https://domain.example/some-context",
  "summary": "<some-object> exists in <some-context>."
}

You wish to participate in the same context, so you dereference the context in order to learn more about it:

{
  "@context": "https://www.w3.org/ns/activitystreams",
  "id": "https://domain.example/some-context",
  "attributedTo": "https://domain.example/context-owner",
  "summary": "<some-context> is owned by <context-owner>."
}

You create an object, while copying that context onto your object:

{
  "@context": "https://www.w3.org/ns/activitystreams",
  "id": "https://domain.example/your-object",
  "context": "https://domain.example/some-context",
  "summary": "<your-object> exists in <some-context> as well."
}

Distribution occurs somehow; you may want to notify the <context-owner> or seek their acknowledgement of your object, but these things are out-of-scope of this example. A graph source or dataset containing these two objects may be queried for objects sharing the same context:

{
  "@context": "https://www.w3.org/ns/activitystreams",
  "id": "https://domain.example/results-for-your-query",
  "type": "Collection",
  "summary": "The <results-for-your-query> show that 2 items have a context of <some-context>. They are <some-object> and <your-object>.",
  "totalItems": 2,
  "items": [
    "https://domain.example/some-object",
    "https://domain.example/your-object"
  ]
}

Example 2: Choosing not to participate in the same context

This example demonstrates how objects can have different contexts, indicating that they were created for different purposes. Though they may be grouped by other criteria, they do not share a primary reason for existing.

You encounter the following object:

{
  "@context": "https://www.w3.org/ns/activitystreams",
  "id": "https://domain.example/some-object",
  "context": "https://domain.example/some-context",
  "summary": "<some-object> exists in <some-context>."
}

You want to establish your own context, separately from the current object’s context:

{
  "@context": "https://www.w3.org/ns/activitystreams",
  "id": "https://domain.example/a-different-context",
  "attributedTo": "https://domain.example/you",
  "summary": "<a-different-context> is owned by <you>."
}

You may declare that your object is in some way a response to the object that you encountered, but because the contexts are the same, they do not share a primary grouping:

{
  "@context": "https://www.w3.org/ns/activitystreams",
  "id": "https://domain.example/your-object",
  "inReplyTo": {
    "id": "https://domain.example/some-object",
    "context": "https://domain.example/some-context",
    "summary": "<some-object> exists in <some-context>."
  },
  "context": "https://domain.example/a-different-context",
  "summary": "<your-object> is a response to <some-object>, but <some-object> exists in <some-context> while <your-object> exists in <a-different-context>."
}

Querying replies for the original object might surface your object, but querying the context for the original object will not surface your object.

Later, <some-context> is deleted. In some cases, <some-object> might be garbage-collected, since it has lost its reason or purpose for existing; at best, it is considered orphaned. However, <your-object> continues to exist because it was created in <a-different-context> which still exists.

Example 3: Encountering multiple contexts

This example demonstrates how one might deal with objects that have multiple contexts.

You encounter an object with 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"],
  "summary": "<some-object> exists in <some-context> and <some-other-context>."
}

You dereference the two contexts:

{
  "@context": "https://www.w3.org/ns/activitystreams",
  "id": "https://domain.example/some-context",
  "attributedTo": "https://domain.example/context-owner",
  "summary": "<some-context> is owned by <context-owner>."
}
{
  "@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 owned by <other-context-owner>."
}

As a third-party observer, you can choose to browse either context.

As a third-party interactor, you can choose to declare an object in either context, both contexts, a different context, or no context.

The protocol considerations for which contexts are considered valid or acceptable are out-of-scope of this FEP, but dereferencing the contexts can provide more information that can help you make this choice. Perhaps you expect a certain type to be declared, or perhaps you require an owner, or perhaps some other criteria is enforced.

Example 4: Publishing, consuming, and interacting with authoritative contexts that have canonical collections

This example demonstrates how one might expose all objects acknowledged by a context owner to exist within the 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",
  "summary": "<some-object> exists in <some-context>."
}

You wish to browse that 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",
  "type": "https://w3id.org/fep/xxxx/Conversation",
  "https://w3id.org/fep/xxxx/posts": {
    "id": "https://domain.example/some-context/posts",
    "type": "OrderedCollection",
    "items": [
      "https://domain.example/some-object",
      // ...
    ]
  }
  "summary": "<some-context> is owned by <context-owner>. It is a <Conversation> and it has a canonical collection of <posts>, which is <some-context/posts>."
}

As a consumer, you can browse or backfill the conversation by loading the context’s canonical collection. In the above representation of <some-context>, the use of the hypothetical https://w3id.org/fep/xxxx/Conversation type would indicate that the associated canonical collection is exposed via the hypothetical https://w3id.org/fep/xxxx/posts property.


Appendix C: Creating and maintaining contexts and their associated collections using ActivityPub C2S

(This section is non-normative.)

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:

  1. Create the canonical Collection that will be associated with the context. Save the generated Collection id to be used in the next step.
  2. 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 the id from the previous step. Save the generated context id to be used in the next step.
  3. Create the Object that will exist within the context, and specify the context as the id from the previous step. Set an appropriate audience or use to/cc to deliver the Create activity as-is. Save the generated object id to be used in the next step.
  4. Add the Object to the context’s canonical Collection, using the ids obtained from the responses for steps 1 and 3. You may wish to deliver this Add activity via to/cc/audience targeting your intended recipients, especially if you did not deliver the Create Object from step 3.

References

CC0 1.0 Universal (CC0 1.0) Public Domain Dedication

To the extent possible under law, the authors of this Fediverse Enhancement Proposal have waived all copyright and related or neighboring rights to this work.