Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Revisit the https://www.w3.org/ns/activitystreams#Public == as:Public == Public equivalence and guidance #404

Open
trwnh opened this issue Dec 20, 2023 · 13 comments
Assignees
Labels
Needs Group Input/Decision Needs Primer Page Needs a page in the ActivityPub primer Next version Normative change, requires new version of spec

Comments

@trwnh
Copy link

trwnh commented Dec 20, 2023

Motivation

Continuing from #264 and with this JSON-LD playground link, we make the following observations:

  1. First, that the AS2-Core spec requires that AS2 documents are consistent with the JSON-LD compacted form;
  2. Second, that within the playground we observe that the full IRI always gets compacted as as:Public and never as Public;
  3. Third, that Public actually does not expand to the full IRI in any circumstance!

We further observe, from the JSON-LD spec:

  1. Fourth, that per JSON-LD 1.1 Section 3.2 "IRIs" Note 2, a term definition only has an effect when encountered within @type or as a property name, or "elsewhere that a string is interpreted as a vocabulary item". So the "Public": "as:Public" term definition has no current effect.
  2. Fifth, that per JSON-LD 1.1 Section 4.2.3 "Type Coercion", there are two ways to interpret a string value as an IRI -- @type: @id or @type: @vocab. The former (ID value) will only process IRIs, i.e. it will only process when a colon is present. The latter (Vocab value) will first try to interpret the string as a term. Thus, we can override the behavior observed in observation 4, provided that we use @type: @vocab. (See next comment for more exploration.)

Relevant quotes

1.4 Terminology:

A relative IRI reference is an IRI reference that is relative to some other IRI, typically the base IRI of the document. Note that properties, values of @type, and values of terms defined to be vocabulary relative are resolved relative to the vocabulary mapping, not the base IRI.

A term definition is an entry in a context, where the key defines a term which may be used within a map as a key, type, or elsewhere that a string is interpreted as a vocabulary item.

3.2 IRIs:

Properties, values of @type, and values of properties with a term definition that defines them as being relative to the vocabulary mapping, may have the form of a relative IRI reference, but are resolved using the vocabulary mapping, and not the base IRI.

An IRI is generated for the string value of any key for which there are coercion rules that contain an @type key that is set to a value of @id or @vocab.

4.2.3 Type Coercion:

Type coercion is specified within an expanded term definition using the @type key. The value of this key expands to an IRI. Alternatively, the keyword @id or @vocab may be used as value to indicate that within the body of a JSON-LD document, a string value of a term coerced to @id or @vocab is to be interpreted as an IRI. The difference between @id and @vocab is how values are expanded to IRIs. @vocab first tries to expand the value by interpreting it as term. If no matching term is found in the active context, it tries to expand it as an IRI or a compact IRI if there's a colon in the value; otherwise, it will expand the value using the active context's vocabulary mapping, if present. Values coerced to @id in contrast are expanded as an IRI or a compact IRI if a colon is present; otherwise, they are interpreted as relative IRI references.

Pitch

In summary, the logical conclusion is as follows: Pick one of the following.

Make no changes; use as:Public

Per the 1st and 2nd observations, we make no changes but instead make the following guidance:

Implementations MUST use as:Public in order to be consistent with the compacted form; however, they SHOULD treat https://www.w3.org/ns/activitystreams#Public and Public as equivalent for compatibility with existing applications (and these uses should be phased out or dispreferred)

Per the 3rd and 4th observation, the term definition for Public => as:Public can be removed.

Change to @type: @vocab; use Public

Per the 5th observation, we can change the @type of to, cc, audience, bto, bcc from @type: @id to @type: @vocab. This would result in the Public term definition actually taking effect. Hence:

Implementations MUST use Public in order to be consistent with the compacted form; however, they SHOULD treat https://www.w3.org/ns/activitystreams#Public and as:Public as equivalent for compatibility with existing applications (and these uses should be phased out or dispreferred)

Make a normative change; use https://www.w3.org/ns/activitystreams#Public

This is more of a breaking change, and as explained above, unfavourable. In order to ensure consistency with the compacted form, it requires dropping the as term definition / prefix entirely, and rewriting the context document to stop using compact IRIs in all @id definitions. This would result in https://www.w3.org/ns/activitystreams#Public being the only valid representation. Hence:

Implementations MUST use https://www.w3.org/ns/activitystreams#Public; however, they SHOULD treat Public and as:Public as equivalent for compatibility with existing applications (and these uses should be phased out or dispreferred)

@trwnh
Copy link
Author

trwnh commented Dec 23, 2023

For anyone curious, here's what it would take for the inclusion of Public to actually make sense:

  • Dropping the as prefix entirely
  • Using @base = https://www.w3.org/ns/activitystreams/ (and rewriting all @id IRIs to avoid using compact IRIs)

Such an example is here:

https://json-ld.org/playground/#startTab=tab-compacted&copyContext=true&json-ld={"@context":[{"@base":"https://www.w3.org/ns/activitystreams/","Public":{"@id":"https://www.w3.org/ns/activitystreams/Public"},"to":{"@id":"https://www.w3.org/ns/activitystreams/to","@type":"@id"},"cc":{"@id":"https://www.w3.org/ns/activitystreams/cc","@type":"@id"},"audience":{"@id":"https://www.w3.org/ns/activitystreams/audience","@type":"@id"}}],"to":"Public","audience":"https://www.w3.org/ns/activitystreams/Public"}

Obviously, this is completely untenable and would break a ton of things, as well as making all @id values potentially relative to this @base. So there's no way to get Public to "work right". It has to be as:Public (or, if the prefix were removed, https://www.w3.org/ns/activitystreams#Public)

EDIT: Apparently it can be done with @type: @vocab!

https://json-ld.org/playground/#startTab=tab-expanded&copyContext=true&json-ld={"@context":[{"as":"https://www.w3.org/ns/activitystreams#","Public":"https://www.w3.org/ns/activitystreams#Public","to":{"@id":"https://www.w3.org/ns/activitystreams#to","@type":"@vocab"},"cc":{"@id":"https://www.w3.org/ns/activitystreams#cc","@type":"@vocab"},"audience":{"@id":"https://www.w3.org/ns/activitystreams#audience","@type":"@vocab"}}],"to":["Public","https://example.com/1"],"cc":["as:Public","https://example.com/2"],"audience":["https://www.w3.org/ns/activitystreams#Public","https://example.com/3"]}

JSON-LD 1.1 Section 4.2.3 "Type Coercion" describes this in further detail: https://www.w3.org/TR/json-ld11/#type-coercion

Examples 65 and 66 show this in further detail.

In order to make use of this, the context document needs to change to, cc, audience (and bto and bcc?) from @type: @id to @type: @vocab. As explained in the above section, @type: @vocab is like @type: @id but it does term expansion first (whereas @type: @id will expand against @base instead)

Example doc:

{
  "@context": [
    {
      "as": "https://www.w3.org/ns/activitystreams#",
      "Public": "https://www.w3.org/ns/activitystreams#Public",
      "to": {
        "@id": "https://www.w3.org/ns/activitystreams#to",
        "@type": "@vocab"
      },
      "cc": {
        "@id": "https://www.w3.org/ns/activitystreams#cc",
        "@type": "@vocab"
      },
      "audience": {
        "@id": "https://www.w3.org/ns/activitystreams#audience",
        "@type": "@vocab"
      }
    }
  ],
  "to": [
    "Public",
    "https://example.com/1"
  ],
  "cc": [
    "as:Public",
    "https://example.com/2"
  ],
  "audience": [
    "https://www.w3.org/ns/activitystreams#Public",
    "https://example.com/3"
  ]
}

Expands to:

[
  {
    "https://www.w3.org/ns/activitystreams#audience": [
      {
        "@id": "https://www.w3.org/ns/activitystreams#Public"
      },
      {
        "@id": "https://example.com/3"
      }
    ],
    "https://www.w3.org/ns/activitystreams#cc": [
      {
        "@id": "https://www.w3.org/ns/activitystreams#Public"
      },
      {
        "@id": "https://example.com/2"
      }
    ],
    "https://www.w3.org/ns/activitystreams#to": [
      {
        "@id": "https://www.w3.org/ns/activitystreams#Public"
      },
      {
        "@id": "https://example.com/1"
      }
    ]
  }
]

Compacts to:

{
  "@context": {
    "as": "https://www.w3.org/ns/activitystreams#",
    "Public": "https://www.w3.org/ns/activitystreams#Public",
    "to": {
      "@id": "https://www.w3.org/ns/activitystreams#to",
      "@type": "@vocab"
    },
    "cc": {
      "@id": "https://www.w3.org/ns/activitystreams#cc",
      "@type": "@vocab"
    },
    "audience": {
      "@id": "https://www.w3.org/ns/activitystreams#audience",
      "@type": "@vocab"
    }
  },
  "audience": [
    "Public",
    "https://example.com/3"
  ],
  "cc": [
    "Public",
    "https://example.com/2"
  ],
  "to": [
    "Public",
    "https://example.com/1"
  ]
}

@ap-socialhub
Copy link
Collaborator

This issue has been mentioned on SocialHub. There might be relevant details there:

https://socialhub.activitypub.rocks/t/fep-7502-limiting-visibility-to-authenticated-actors/3784/2

@TallTed
Copy link
Member

TallTed commented Dec 27, 2023

@nightpool — Please edit your #404 (comment) and codefence every instance of any @word (single backticks will do, a la `@context`), including those in quoteblocks, such that every comment made here does not ping those GitHub users who did not choose to participate in this discussion.

@trwnh
Copy link
Author

trwnh commented Dec 27, 2023

One thing I am not entirely clear on wrt @type: @vocab is whether it makes sense to close the door entirely on addressing IRIs relative to the @base. For example there is the possibility of doing something like

@context: [
  activitystreams, {
    @base: https://example.com/
  }]
to: [users/1, users/2]

or doing something like

GET https://example.com/actor

@context: activitystreams
inbox: inbox  # expands to https://example.com/actor/inbox
outbox: outbox  # expands to https://example.com/actor/outbox

My immediate take is that these relative IRI references should probably be avoided, even though they are valid. But in order to keep the possibility open, one would have to go with the "do nothing, use as:Public" option (and keep the coercion as @type: @id)

@nightpool
Copy link
Collaborator

nightpool commented Dec 27, 2023 via email

@TallTed
Copy link
Member

TallTed commented Dec 27, 2023

@nightpool — Please edit your new #404 (comment) and delete the quoted email content, which includes a number of @name, which cannot be codefenced because GitHub doesn't handle Markdown in emailed comments. Sorry to keep banging on this drum, but it's the only way to prevent pinging those uninvolved GitHub users.

@nightpool
Copy link
Collaborator

nightpool commented Dec 27, 2023 via email

@trwnh
Copy link
Author

trwnh commented Dec 27, 2023

2.2 of as2-core says

Relative IRI (and URL) references SHOULD NOT be used within an Activity Streams 2.0 document due to the fact that many JSON parser implementations are not capable of reliably preserving the base context necessary to properly resolve relative references.

so that's a SHOULD NOT, not a MUST NOT, but good catch anyway! still worth keeping in mind that type:vocab makes things relative to the vocab mapping and not the base mapping.

@TallTed
Copy link
Member

TallTed commented Apr 10, 2024

"expected" language is good for non-normative discussion (primer or the like), instead of "should" or "must" that are often read as normative even in non-normative documents (or non-normative sections of normative documents) and even when lowercase

@evanp
Copy link
Collaborator

evanp commented Apr 10, 2024

I think the best steps for us right now are as follows:

  1. A Primer page that lays out the guidance in option 1 -- that publishers ought to use as:Public for maximum interop, and that consumers ought to expect all three options.

  2. We consider the normative change of adding @type: @vocab to the AS2 context document once a new working group is called. We can mark this issue as "Next version" so we remember to consider it at that time (even if it's closed).

  3. Review the AS2 and AP specs for examples that use Public or the https://www.w3.org/ns/activitystreams#Public and consider making ERRATA for those examples, since they are technically not compliant since they aren't equivalent to the compacted form.

@evanp evanp self-assigned this Apr 10, 2024
@evanp evanp added Needs Primer Page Needs a page in the ActivityPub primer Next version Normative change, requires new version of spec Needs errata We need to add errata for this labels Apr 10, 2024
@trwnh
Copy link
Author

trwnh commented Oct 10, 2024

2. We consider the normative change of adding @type: @vocab to the AS2 context document once a new working group is called. We can mark this issue as "Next version" so we remember to consider it at that time (even if it's closed).

Speaking of considerations and next versions, a more ideal path forward might be to consider not putting the Public pseudo-collection in the addressing properties at all, but instead defining a new property visibilityClass/readClass which can be either Public or null/missing (or extended with other classes like acl:AuthenticatedAgent, etc). This is kind of like a halfway-point between what we have now vs. adopting an additional access control framework like Web Access Control or something inspired by it (since WAC might not be ideal in its current form wholesale... it's certainly more verbose than it needs to be for a simple "visibility check"!)

If we go "all the way" and adopt WAC in some form, it could look something like this at ~minimum:

{
  "@context": [
    "https://www.w3.org/ns/activitystreams",
    {
      "acl": "https://www.w3.org/ns/auth/acl#",
      "accessControl": {
        "@id": "acl:accessControl",
        "@type": "@id"
      },
      "aclMode": {
        "@id": "acl:mode",
        "@type": "@vocab"
      },
      "agentClass": {
        "@id": "acl:agentClass",
        "@type": "@vocab"
      },
      "agent": {
        "@id": "acl:agent",
        "@type": "@id"
      },
      "Agent": "http://xmlns.com/foaf/0.1/Agent", // this is technically the same thing as Public, so it could be considered redundant, but presumably the WAC side of things will include this anyway
      "AuthenticatedAgent": "acl:AuthenticatedAgent",
      "accessTo": {
        "@id": "acl:accessTo",
        "@type": "@id"
      }
    }
  ],
  "id": "https://domain.example/some-object",
  "type": "Note",
  "attributedTo": "https://domain.example/the-author",
  "content": "This is Public, as signaled by WAC and the ACL ontology",
  "accessControl": [
    {
      "type": "acl:Authorization",
      "agentClass": ["Public", "Agent"], // these mean the same thing
      "aclMode": "acl:Read",
      "accessTo": "https://domain.example/some-object"
    },
    {
      "type": "acl:Authorization",
      "agent": "https://domain.example/the-author",
      "aclMode": ["acl:Read", "acl:Write"],
      "accessTo": "https://domain.example/some-object"
    }
  ]
}

If we go with something more bespoke:

{
  "@context": [
    "https://www.w3.org/ns/activitystreams",
    {
      "visibilityClass": {
        "@id": "as:visibilityClass",
        "@type": "@vocab"
      }
    }
  ],
  "id": "https://domain.example/some-object",
  "type": "Note",
  "attributedTo": "https://domain.example/the-author",
  "content": "This is Public, as signaled by a new property",
  "visibilityClass": "Public"  // no more as:Public, this actually uses the vocab mapping
}

Over time, consumers should be encouraged to support this new property. After some time has passed, producers should be discouraged from addressing as:Public and encouraged to use the new property. Producers can also adopt the property early if they are okay with falling back to having their activities and objects be interpreted as non-public.

There's no seamless way to do this, but that's more because addressing as:Public was a mistake and should not have been used to signal visibility. There is a slight consideration for an exemption, which is to have clients explicitly signal that they are opting into the "deliver to all shared inboxes" behavior that is lightly touched upon in the spec as a MAY.

@evanp
Copy link
Collaborator

evanp commented Dec 13, 2024

We did not find any examples of Public in AS2 core or vocabulary.

Uses of full URI in AP:

  • example 6
  • example 10
  • explanatory note in 5.6 "Public Addressing"
  • example 15
  • example 16

I've added errata for these examples and note to https://www.w3.org/wiki/ActivityPub_errata/Proposed

  • In Examples 6, 10, 15, 16 the public collection address should be compacted to "as:Public".
  • The Note in section 5.6 "Public Addressing" should read Compacting an ActivityStreams object using the ActivityStreams JSON-LD context will result in "https://www.w3.org/ns/activitystreams#Public" being represented as "as:Public". Publishers might erroneously produce representations with the full URI or with the unprefixed value "Public". To maximize interoperability, consumers can accept all three representations.

@evanp evanp added Needs Group Input/Decision and removed Needs errata We need to add errata for this labels Dec 13, 2024
@TallTed
Copy link
Member

TallTed commented Dec 13, 2024

I might suggest (without the parenthetical) —

To maximize interoperability (and comply with Postel's Law), consumers SHOULD accept all three representations.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Needs Group Input/Decision Needs Primer Page Needs a page in the ActivityPub primer Next version Normative change, requires new version of spec
Projects
None yet
Development

No branches or pull requests

5 participants