-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.html
1291 lines (1250 loc) · 85.6 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>ActivityPub Discovery</title>
<script src="https://www.w3.org/Tools/respec/respec-w3c" class="remove" defer></script>
<script class="remove">
// All config options at https://respec.org/docs/
var respecConfig = {
specStatus: "CG-DRAFT",
latestVersion: null,
editors: [{ name: "Evan Prodromou", url: "https://socialwebfoundation.org/@evanp" }],
github: "swicg/activitypub-html-discovery",
shortName: "apdisco",
xref: "web-platform",
group: "socialcg",
noRecTrack: true
};
</script>
</head>
<body>
<section id="abstract">
<p>ActivityPub is a standard for publishing structured social network data on the Web in JSON-LD format. This
document describes various methods for discovering the ActivityPub object described by an HTML page, and
conversely the HTML page for an ActivityPub object.</p>
</section>
<section id="sotd">
<p>This is a draft of the Social Web Incubator Group (SocialCG) Discovery Task Force.</p>
</section>
<section class="informative">
<h2>Introduction</h2>
<p>ActivityPub is a standard for publishing structured social network data on the Web in JSON-LD format and sharing that data from client to server and from server to server. This document describes several methods for discovering the ActivityPub object described by an HTML page, and conversely the HTML page for an ActivityPub object.</p>
<p>
Social data in the ActivityPub model is a "resource" like a person, image, or place. That resource has an ActivityPub JSON-LD representation (if it doesn't, it's not covered by this document!) and may have an HTML representation. The ActivityPub representation and the HTML representations each have an URL -- possibly the same URL.
</p>
<p>
"Forward discovery" is any process that, given the resource's HTML representation, will return the resource's ActivityPub representation.
</p>
<ul>
<li>resource HTML → resource ActivityPub</li>
</ul>
<p>
There are multiple techniques for forward discovery which will be documented in this report. Some discovery techniques require the full HTML document -- its markup and content. Others only need the URL of the HTML representation.
</p>
<ul>
<li>resource HTML document → resource ActivityPub</li>
<li>resource HTML URL → resource ActivityPub</li>
</ul>
<p>
When a consumer is doing discovery, it can start with the input it has -- document or URL -- and convert to the other input if it needs to.Converting an HTML document to an HTML URL usually requires access to the document's context, like a browser environment. Converting an HTML URL to a document requires fetching the document and parsing it.
</p>
<ul>
<li>resource HTML document → resource HTML URL → resource ActivityPub</li>
<li>resource HTML URL → resource HTML document → resource ActivityPub</li>
</ul>
<p>
"Reverse discovery" is any process that, given the resource's ActivityPub representation, will return the resource's HTML representation.
</p>
<ul>
<li>resource ActivityPub → resource HTML</li>
</ul>
<p>
As with forward discovery, some reverse discovery techniques require the full ActivityPub document -- its JSON properties and content. Others only need the URL of the ActivityPub representation.
</p>
<ul>
<li>resource ActivityPub document → resource HTML</li>
<li>resource ActivityPub URL → resource HTML</li>
</ul>
<p>
When a consumer is doing reverse discovery, it can start with techniques that use the input it has -- document or URL -- and switch to techniques that use the other input only if needed. Converting an ActivityPub document to an ActivityPub URL requires extracting the <code>id</code> property of the object. Converting an ActivityPub URL to a document requires fetching the document and parsing it.
</p>
<ul>
<li>resource ActivityPub document → resource ActivityPub URL → resource HTML</li>
<li>resource ActivityPub URL → resource ActivityPub document → resource HTML</li>
</ul>
<p>
Some resources have a relationship to another resource, which is its <dfn>author</dfn> or creator. The author resource has an ActivityPub JSON-LD representation (again, if it doesn't, it's not covered by this document!) and may have an HTML representation.
</p>
<p>
"Author discovery" is any process that, given the resource's HTML representation, will return the author resource's ActivityPub representation. Author discovery is important because many ActivityPub processes require delivering activities to the author.
</p>
<p>
There are a few paths for author discovery:
</p>
<ul>
<li>resource HTML → resource ActivityPub → author ActivityPub</li>
<li>resource HTML → author HTML → author ActivityPub</li>
<li>resource HTML → author ActivityPub</li>
</ul>
<p>
Note that a resource may have an author resource with an ActivityPub representation, but not have its own ActivityPub representation. An example is an article published in a content-management system (CMS) that is ascribed to an actor with an ActivityPub account who wants to receive credit and/or feedback for the work.
</p>
<p>As with forward and reverse discovery, the consumer may start with the URL of the resource HTML or the content of the resource HTML.</p>
<p>
For some kinds of resources, especially ActivityPub actors, a Webfinger is a more readable alternative to the ActivityPub representation's URL. In these cases, the forward discovery process and author discovery process might include the Webfinger discovery process as an intermediate step:
</p>
<ul>
<li>resource HTML → resource Webfinger → resource ActivityPub</li>
<li>resource HTML → author Webfinger → author ActivityPub</li>
</ul>
<p>
In this document, the terms "<a href="https://www.w3.org/TR/activitystreams-core/#publishers">publisher</a>" and "<a href="https://www.w3.org/TR/activitystreams-core/#consumers">consumer</a>" are used as in Activity Streams 2.0 Core. The terms are extended to include implementations that publish or consume HTML representations of resources with ActivityPub representations.
</p>
<p>
In this document, we describe several methods of forward discovery, reverse discovery, and author discovery. Different methods are implemented by different publishers and consumers, and have different trade-offs in terms of complexity, performance, and reliability. A section on verification explains how to verify that the discovered information is accurate. The final sections define best practices for publishers and consumers to maximize interoperability and minimize development effort.
</p>
<section>
<h3>Applicable resource types</h3>
<p>Unless otherwise specified, the techniques described below can be used with any Activity Streams 2.0 types. The best-defined groups of AS2 types for HTML discovery are actor types:</p>
<ul>
<li><code>Person</code></li>
<li><code>Application</code></li>
<li><code>Service</code></li>
<li><code>Group</code></li>
<li><code>Organization</code></li>
</ul>
<p>
and digital content types:
</p>
<ul>
<li><code>Note</code></li>
<li><code>Article</code></li>
<li><code>Image</code></li>
<li><code>Video</code></li>
<li><code>Audio</code></li>
<li><code>Document</code></li>
<li><code>Page</code></li>
</ul>
<p>
Other ActivityPub types are less likely to have their own HTML representations, such as activity types.
</p>
<p>
<code>Collection</code> types are often better represented by an object they are closely related to. For example, an actor's <code>outbox</code>
collection is often provided on the actor's profile page, which is a representation of the actor. Similarly, the <code>likes</code> or <code>replies</code> of an <code>Image</code> object are often provided on the object's page, and don't have an independent HTML representation. That said, this document does not preclude the possibility of HTML representations for collection types.
</p>
</section>
<section>
<h3>Motivating user stories</h3>
<p>These are some of the user stories that motivate this work.</p>
<p>
<ul>
<li><em>As a Web user, when viewing a Web page, I want to <code>Like</code> the contents, so that I can share it with my followers, let the author know I appreciated it, and save it to my <code>liked</code> collection.</em> A browser-based ActivityPub API client could submit a <code>Like</code> activity to the user's ActivityPub server, but it would need to know the ID of the ActivityPub equivalent of the page.</li>
<li><em>As a Web user, when viewing a Web page, I want to <code>Announce</code> the contents, so that I can share it with my followers.</em> A browser-based ActivityPub API client could submit a <code>Announce</code> activity to the user's ActivityPub server, but it would need to know the ID of the ActivityPub equivalent of the page.</li>
<li><em>As a Web user, when viewing an actor's profile, I want to <code>Follow</code> the actor, so that I can get updates about their activities in my inbox.</em> A browser-based ActivityPub API client could submit a <code>Follow</code> activity to the user's ActivityPub server, but it would need to know the actor ID of the actor whose profile is being viewed.</li>
<li><em>As a Web user, when viewing an actor's profile, I want to send them a direct message, to share information or make a connection.</em> A browser-based ActivityPub API client could send a <code>Note</code> activity to the user's ActivityPub server, with the profile actor's ID in the <code>to</code> property, but the API client would need to know the actor ID of the actor whose profile is being viewed.</li>
<li><em>As a Web user, when viewing an image page in my browser, I want to send a direct message to the author, to share information or give feedback.</em> A browser-based ActivityPub API client could send a <code>Note</code> activity to the user's ActivityPub server, with the image's author's actor ID in the <code>to</code> property, but the API client would need to know the actor ID of the author of the image.</li>
<li><em>As a social network client user, when I see a link in the content of a social media post, I want to load the resource for that link into my social media client without launching an external browser, so that I have a smooth user experience and can fully interact with the content.</em> Given a link like <code>https://html.example/blog/page-1.html</code>, an ActivityPub API client could discover the related ActivityPub ID <code>https://ap.example/api/page-1.jsonld</code>, retrieve it with machine-readable metadata, and provide affordances for interacting with the object, such as liking, sharing, or replying.</li>
<li><em>As a social network client user, when I see a Webfinger handle mentioned in a social media post, I want to follow a link to the actor's profile page, so I can learn more about them.</em> Converting a Webfinger address into an ActivityPub actor ID is well-covered in the ActivityPub and Webfinger report, but the next step of converting an actor ID to a profile page is not.</li>
<li><em>As a social network client user, when I see a Webfinger handle mentioned in a social media post, I want to load the actor's profile into my social network client, so I can interact directly with it.</em> If the link in the <code>content</code> is to the actor's profile page, it's necessary to be able to turn that link into an actor ID to allow more inspection of the actor and affordances like following or blocking.</li>
</ul>
</p>
</section>
<section>
<h3>URLs in examples</h3>
<p>This document uses a consistent format for example URLs:
<pre>
<code>https://{name}.example/{path}/{type}-{ordinal}{?ext}</code>
</pre>
</p>
<p>Where:</p>
<ul>
<li><code>{name}</code> is the domain name of the server. There are three default domain names used:
<ul>
<li><code>ap.example</code> - A server that primarily provides ActivityPub JSON-LD documents.</li>
<li><code>html.example</code> - A server that primarily provides HTML documents.</li>
<li><code>mixed.example</code> - A server that provides both ActivityPub JSON-LD and HTML documents.</li>
</ul>
Other names may be used when additional examples are required.
</li>
<li><code>{path}</code> is the path to the object. It should be opaque; none of the paths in this document have
semantic meaning unless otherwise specified.</li>
<li><code>{type}</code> is the content type of the resource. This will usually be the lowercase version of the
ActivityPub object type, such as <code>Note</code>, <code>Person</code>, or <code>Image</code>.</li>
</li>
<li><code>{ordinal}</code> is an ordinal number, when multiple objects are being described in the same
discussion.</li>
<li><code>{ext}</code> is an optional "file extension" that indicates the Internet media type of the resource,
including:
<ul>
<li><code>.jsonld</code> for JSON-LD objects</li>
<li><code>.html</code> for HTML documents</li>
<li><code>.png</code> for PNG images, <code>.jpg</code> for JPEG images, etc.</li>
</ul>
Extensions may be left off of the URL, especially if the same URL will be used for multiple media types. Note that including the <code>.jsonld</code> extension is not common practice for ActivityPub <code>id</code> values. It is used in this report to highlight that the URL is for a JSON-LD representation.
</li>
</ul>
<p>The structure used in the examples is merely mnemonic and non-normative. None of the techniques described in this document depend on a particular URL structure, unless otherwise specified.</p>
</section>
</section>
<section id="discovery">
<h2>HTML to ActivityPub</h2>
<p>This form of discovery, <dfn>forward discovery</dfn>, will identify an ActivityPub JSON-LD resource based on the HTML representation of the object.</p>
<section>
<h3>URL as input</h3>
<p>These discovery techniques require an URL as input. Consumers may start with URLs if they are extracting links from RSS feeds or microblogging content, or when converting from other social networking platform content.</p>
<section>
<h4>Content negotiation</h4>
<p><a href="https://en.wikipedia.org/wiki/Content_negotiation">Content negotiation</a> is a catch-all term for
ways of negotiating the representation of a resource through the HTTP protocol. In this document, it will
specifically cover <a href="https://datatracker.ietf.org/doc/html/rfc7231#section-3.4.1">proactive negotiation</a>
using the <a href="https://datatracker.ietf.org/doc/html/rfc7231#section-5.3.2">Accept</a> header.
</p>
<p>
Given the URL for an HTML document, such as <code>https://mixed.example/some/path/to/note-1</code>, a consumer
could attempt to retrieve the corresponding ActivityPub JSON-LD object using this HTTP request:
</p>
<pre class="HTTP">
GET /some/path/to/note-1 HTTP/1.1
Host: mixed.example
Accept: application/activity+json, application/ld+json, application/json
</pre>
<p>
A compliant server may respond with the ActivityPub JSON-LD object in the body of the response:
</p>
<pre class="HTTP">
HTTP/1.1 200 OK
Content-Type: application/activity+json
{
"@context": "https://www.w3.org/ns/activitystreams",
"id": "https://mixed.example/some/path/to/note-1",
"type": "Article",
"content": "This is a note."
}
</pre>
<p>
This is typically used when the ActivityPub server and the HTML server are implemented in the same software package.
Because this has historically been the case for many implementations, some consumers expect this behavior to be the
default.
</p>
<p>
Alternately, the server may respond with a <code>308 Permanent Redirect</code> to indicate the location of the JSON-LD
representation.
</p>
<pre class="HTTP">
HTTP/1.1 308 Permanent Redirect
Location: https://mixed.example/different/path/to/note-1.jsonld
</pre>
<section>
<h5>Content negotiation failure</h5>
<p>
If the server does not support content negotiation, it may respond with a <code>406 Not Acceptable</code> status
code.
</p>
<pre>
HTTP/1.1 406 Not Acceptable
Content-Type: text/plain
No representation matching this request could be found.
</pre>
<p>Less compliant servers may ignore the <code>Accept</code> header altogether and return the HTML content regardless:
</p>
<pre class="HTTP">
HTTP/1.1 200 OK
Content-Type: text/html
<html>
<head>
<title>Note 1</title>
</head>
<body>
<p>This is a note.</p>
</pre>
<p>
A more difficult failure mode to detect arises when the server does not support ActivityPub, but does support content negotiation for another JSON format. Such a server returns a <code>200 OK</code> status code with a JSON
object that does not use JSON-LD, or JSON-LD object that does not use the Activity Streams 2.0 vocabulary:
</p>
<pre class="HTTP">
HTTP/1.1 200 OK
Content-Type: application/json
{
"property": "value",
"otherProperty": "otherValue"
}
</pre>
</section>
</section>
<section>
<h4>HTTP Link header</h4>
<p>
The <a href="https://datatracker.ietf.org/doc/html/rfc8288">HTTP Link header</a> can be used to indicate an
alternative representation of a resource. A consumer can use this header to discover the ActivityPub JSON-LD object
for an HTML page.
</p>
<p>
Given the URL for an HTML document, such as <code>https://html.example/user/test1/article-1</code>, the consumer can
use a HTTP <code>HEAD</code> request to get the headers for the resource, which will hopefully include the
<code>Link</code> header:
</p>
<pre class="HTTP">
HEAD /user/test1/article-1 HTTP/1.1
Host: html.example
</pre>
<p>
A compliant server will respond with the headers for the resource:
</p>
<pre class="HTTP">
HTTP/1.1 200 OK
Link: <https://ap.example/api/articles/article-1.jsonld>; rel="alternate"; type="application/activity+json"
</pre>
<p>
The link header with the <code>alternate</code> relation type, and an ActivityPub-compatible media type, indicates
that the ActivityPub JSON-LD object is available at the linked URL.
</p>
<p>
This can be a very efficient method of discovery, since the consumer does not need to download the entire HTML
document and parse its contents.
</p>
<p>
Servers may also include the <code>Link</code> header in the response to a <code>GET</code> request for the HTML page.
</p>
<pre class="HTTP">
GET /user/test1/article-1 HTTP/1.1
Host: html.example
</pre>
<p>
A compliant server will respond with the headers for the resource:
</p>
<pre class="HTTP">
HTTP/1.1 200 OK
Link: <https://ap.example/api/articles/article-1.jsonld>; rel="alternate"; type="application/activity+json"
Content-type: text/html
<html>
<head>
...
</pre>
<section>
<h5>Link header failure</h5>
<p>
Some servers may return the full body of the HTML document in response to a <code>HEAD</code> request, without
including a <code>Link</code> header.
</p>
<pre class="HTTP">
HTTP/1.1 200 OK
Content-type: text/html
<html>
<head>
...
</pre>
</section>
</section>
<section>
<h3>Webfinger</h3>
<p><a href="https://datatracker.ietf.org/doc/html/rfc7033">Webfinger</a>
is a standard for discovering metadata about a resource identified with an URL. Finding the ActivityPub URL for an actor identified with an <code>acct:</code> URL is well documented in the <a href="https://www.w3.org/community/reports/socialcg/CG-FINAL-apwf-20240608/">ActivityPub and Webfinger</a> report. However, Webfinger can be used to find metadata about other resources, including HTML pages with <code>https:</code> URLs.</p>
<p>Given an URL for a document, like <code>https://html.example/group-1.html</code>, a GET request can be made to an URL in the <code>/.well-known/</code> path of the domain for the URL, as follows:</p>
<pre class="HTTP">
GET /.well-known/webfinger?resource=https%3A%2F%2Fhtml.example%2Fgroup-1.html HTTP/1.1
Host: html.example
</pre>
<p>Note that the <code>/.well-known/webfinger</code> path is fixed and required for Webfinger.</p>
<p>A compliant server will respond with the metadata for the resource:</p>
<pre class="HTTP">
HTTP/1.1 200 OK
Content-Type: application/jrd+json
{
"subject": "https://html.example/group-1.html",
"links": [
{
"rel": "alternate",
"type": "application/activity+json",
"href": "https://ap.example/api/groups/group-1.jsonld"
}
]
}
</pre>
<p>Note that unlike other URLs used in the examples in this report, the <code>/.well-known/webfinger</code> path is fixed and required for Webfinger.</p>
<p>The JRD JSON format includes <a href="https://datatracker.ietf.org/doc/html/rfc7033#section-4.4">a number of properties</a>, as defined in the Webfinger RFC 7033. The relevant data structure in this example is the object in the <code>links</code> array with the <code>rel</code> property set to <code>alternate</code> and the <code>type</code> property set to <code>application/activity+json</code>, an ActivityPub-compatible media type. The <code>href</code> property of this link is URL of the ActivityPub equivalent for the HTML page.</p>
<section>
<h4>Webfinger failure</h4>
<p>Not all Webfinger-aware servers return JRD documents for <code>https</code> URLs. Others might only return JRD documents for URLs that represent actors, such as registered users.</p>
<p>As with other link-relation-based discovery mechanisms, like the HTTP Link header or the <link> element, a JSON or JSON-LD media in the link's <code>type</code> property might not indicate an ActivityPub URL, but some other JSON or JSON-LD object.</p>
</section>
</section>
</section>
<section>
<h3>Document as input</h3>
<p>Alternately, a consumer may start with the full contents of an HTML document, including markup and other content. For example, a browser-based application may have access to the HTML loaded in the browser window. It's also usually possible to extract the URL from the environment -- for example, using the <code>document.location</code> property in a JavaScript environment. But using the document content for discovery can return the ActivityPub equivalent without the HTTP requests that discovery by URL requires, saving some time and network traffic.</p>
<section>
<h4>HTML <link> element</h4>
<p>The <a href="https://html.spec.whatwg.org/multipage/semantics.html#the-link-element">link</a> element is a metadata element used in the <code><head></code> section of an HTML document. It provides
links for the whole document, using a number of different link relations.</p>
<p>To indicate its equivalent ActivityPub object, the HTML page at <code>https://html.example/watch/video-1.html</code> could include the following link element:</p>
<pre class="HTML">
<!doctype html>
<html>
<head>
<title>Video 1</title>
<link
rel="alternate"
type="application/activity+json"
href="https://ap.example/api/descriptors/video-1.jsonld" />
</head>
<body>
<!-- rest of the page -->
</body>
</html>
</pre>
<p>Consumers need to parse the HTML to find the <code>link</code> element with the <code>alternate</code> relation and an ActivityPub-compatible media type as <code>type</code>. This can be slow and complicated.</p>
<section>
<h5>Link element failure</h5>
<p>Some servers may include a <code>link</code> element with an
<code>alternate</code> relation and with a JSON type or JSON-LD type that does not link to an ActivityPub resource.</p>
<pre class="HTML">
<!doctype html>
<html>
<head>
<title>Video 1</title>
<link
rel="alternate"
type="application/json"
href="https://api.example/unrelated/videodescriptor.json" />
</head>
<body>
<!-- rest of the page -->
</body>
</html>
</pre>
</section>
</section>
<section>
<h3>HTML <a> element</h3>
<p>The <a href="https://html.spec.whatwg.org/multipage/text-level-semantics.html#the-a-element">a</a> element is an element used in the <code><body></code> section of an HTML document. It can be used to define relationships with other documents, with the benefit that the link is (usually) visible and clickable by a reader.</p>
<p>To indicate its equivalent ActivityPub object, the HTML page at <code>https://html.example/profiles/person-1.html</code> could include the following <code>a</code> element:</p>
<pre class="HTML">
<!doctype html>
<html>
<head>
<title>Person 1</title>
</head>
<body>
<a
rel="alternate"
type="application/activity+json"
href="https://ap.example/users/person-1.jsonld" >
Actor data for Person 1
</a>
<!-- rest of the page -->
</body>
</html>
</pre>
<p>Consumers will need to parse the HTML to find the <code>a</code> element with the <code>alternate</code> relation and an ActivityPub-compatible media type as <code>type</code>. This can be even more slow and complicated than with the <code>link</code> header. The <code>link</code> header is usually in the first few kilobytes of a document, and will usually be nested only 2 levels below the document in the DOM tree. An <code>a</code> element may be anywhere in the <code>body</code>, maybe nested very deep in the tree.</p>
<section>
<h4>a element failure</h4>
<p>As with the <code>link</code> element, some servers may include an <code>a</code> element with an
<code>alternate</code> relation and with a JSON type or JSON-LD type that does not link to an ActivityPub resource.
</p>
<p>
In addition, many content management systems allow end users to set
<code>rel</code> and other properties on <code>a</code> elements, which may result in false matches. Even more than with other methods, using the <code>a</code> element for discovery requires reverse discovery for confirmation (see <a href="#best-practices-for-consumers">Best practices for consumers</a>).
</p>
</pre>
</section>
</section>
<section>
<h3>Embedded JSON-LD</h3>
<p>HTML documents can include <a href="https://www.w3.org/TR/json-ld11/">JSON-LD</a> data in a <code><script></code> element in the <code><head></code> section of the document. This data can be used to provide metadata about the document, including its equivalent ActivityPub object.</p>
<p>Given a page that shows an image at <code>https://html.example/gallery/image-17.html</code>, the HTML for the page could look like this:</p>
<pre>
<!DOCTYPE html>
<html lang="en">
<head>
<title>Image 17</title>
<script type="application/ld+json">
{
"@context": "https://www.w3.org/ns/activitystreams",
"type": "Image",
"id": "https://ap.example/api/images/image-17.jsonld",
"url": [
{
"type": "Link",
"mediaType": "text/html",
"href": "https://html.example/gallery/image-17.html"
}
]
}
</script>
</head>
<body>
<h1>Image 17</h1>
<p><img src="https://html.example/images/image-17.png"></p>
</body>
</html>
</pre>
<p>This embedded JSON-LD specifies that an ActivityPub object with the ID <code>https://ap.example/api/images/image-17.jsonld</code> exists, and that it has an HTML page <code>url</code> at <code>https://html.example/gallery/image-17.html</code>, that is, the current page's URL. This is a roundabout, but clear, way to specify the ActivityPub ID of the current page.</p>
<p>Consumers need to parse the HTML page, and the embedded JSON-LD, to extract the ActivityPub object ID. An advantage to this technique is that other properties of the ActivityPub object can be embedded as well; however, to confirm those properties, the consumer will need to fetch the object from its canonical URL, the ID, anyways.</p>
<section>
<h4>Embedded JSON-LD failure</h4>
<p>
Complicated structures for the <code>url</code> property may make it hard to confirm that the object's URL is the same as the current page's.
</p>
<p>
Embedded JSON-LD is very popular for embedding <a href="https://schema.org/">Schema.org</a> metadata. This can lead to false positives when looking for ActivityPub objects.
</p>
</section>
</section>
</section>
</section>
<section id="reverse-discovery">
<h2>ActivityPub to HTML</h2>
<p>
<dfn>Reverse discovery</dfn>, in this report, means identifying the HTML
page that represents the same object as an ActivityPub JSON-LD object. This is necessary for user stories like creating a link to an actor in microsyntax.
</p>
<section>
<h3>URL as input</h3>
<p>These techniques require the ActivityPub object's URL as input. Often, the object's URL is obtained either as a property of another ActivityPub object, or from the <code>id</code> property of the ActivityPub JSON-LD document.</p>
<p>If these techniques aren't successful, the consumer can use the URL to fetch the ActivityPub JSON-LD document, and then use a reverse discovery technique that takes a document as input.</p>
<section>
<h4>Content negotiation</h4>
<p>As with forward discovery, it's possible for the HTML and JSON-LD representations of an object to be found at the same URL.</p>
<section>
<h5>Examples</h5>
<p>
Given the URL for an ActivityPub object, such as <code>https://mixed.example/some/path/to/note-1</code>, a consumer
could attempt to retrieve the corresponding HTML resource using this HTTP request:
</p>
<pre class="HTTP">
GET /some/path/to/note-1 HTTP/1.1
Host: mixed.example
Accept: text/html
</pre>
<p>
A compliant server may respond with the HTML document in the body of the response:
</p>
<pre class="HTTP">
HTTP/1.1 200 OK
Content-Type: text/html
<html>
<head>
<title>Note 1</title>
</head>
<body>
<p>This is a note.</p>
</pre>
<p>
Alternately, the server may respond with a <code>308 Permanent Redirect</code> to indicate the location of the HTML
representation.
</p>
<pre class="HTTP">
HTTP/1.1 308 Permanent Redirect
Location: https://mixed.example/different/path/to/note-1.html
</pre>
</section>
<section>
<h5>Content negotiation failure</h5>
<p>
If the server does not support content negotiation, it may respond with a <code>406 Not Acceptable</code> status
code.
</p>
<pre>
HTTP/1.1 406 Not Acceptable
Content-Type: text/plain
No representation matching this request could be found.
</pre>
<p>Less compliant servers may ignore the <code>Accept</code> header altogether and return the JSON-LD content regardless:
</p>
<pre class="HTTP">
HTTP/1.1 200 OK
Content-Type: application/activity+json
{
"@context": "https://www.w3.org/ns/activitystreams",
"id": "https://mixed.example/some/path/to/note-1",
"type": "Note",
"content": "This is a note."
}
</pre>
<p>
</section>
</section>
<section>
<h4>HTTP Link header</h4>
<p>As with forward discovery, it is possible to use the <code>Link</code> header to identify an HTML page related to a given ActivityPub JSON-LD resource. A <code>Link</code> header with the <code>alternate</code> link relation and a <code>type</code> equal to <code>text/html</code> indicates an HTML page representing the same object.</p>
<p>The advantage of this technique is that it does not require downloading and parsing the JSON-LD content of the ActivityPub object. The <code>Link</code> header has fewer options for formatting than other methods such as the <code>url</code> property, for example, making it slightly easier for consumers.</p>
<section>
<h5>Example</h5>
<p>Given an ActivityPub JSON-LD object at <code>https://ap.example/some/path/person-1.jsonld</code>, a consumer could use a <code>HEAD</code> HTTP request to get the relevant headers for the resource:</p>
<pre class="HTTP">
HEAD /some/path/person-1.jsonld HTTP/1.1
Host: ap.example
</pre>
<p>The publisher would respond with the HTTP headers, including a <code>Link</code> header:</p>
<pre class="HTTP">
HTTP/1.1 200 OK
Content-Type: application/activity+json
Link: <https://html.example/profiles/person-1.html>; rel="alternate"; type="text/html"
</pre>
</section>
<section>
<h5>HTTP Link header failure</h5>
<p>Some non-compliant HTTP servers will send the full body of the resource in the response to the <code>HEAD</code> request.</p>
</section>
</section>
<section>
<h4>Webfinger</h4>
<p>The Webfinger protocol can be used to find an HTML page related to an ActivityPub object in a number of ways.</p>
<p>The consumer can identify the resource for the a Webfinger query in two ways. First, the <code>id</code> property, usually an <code>https</code> URL, can be passed as the <code>resource</code> parameter for the Webfinger query. Alternately, if the ActivityPub object is an <a href="https://www.w3.org/TR/activitypub/#actors">actor</a>, an <code>acct</code> URL in the format <code>acct:[email protected]</code> can be constructed using the technique for <a href="https://www.w3.org/community/reports/socialcg/CG-FINAL-apwf-20240608/#reverse-discovery">Webfinger reverse discovery</a>. This <code>acct</code> URL can be used as the <code>resource</code> parameter for the Webfinger query.</p>
<p>The publisher can provide a link to the HTML representation of the object in the JRD output of the Webfinger query in at least two ways.
</p>
<p>
First, the <code>links</code> property of the output object can contain a link object with a <code>rel</code> property set to <code>alternate</code> and the <code>type</code> property set to <code>text/html</code>. If such a link exists, its <code>href</code> property is the URL of the related HTML page.
</p>
<p>Second, the <code>links</code> property of the JRD output object may include an object with a <code>rel</code> property set to <code>http://webfinger.net/rel/profile-page</code>. This is defined to be "the main home/profile page that a human should visit when getting info about that webfinger account." (<a href="https://webfinger.net/rel/">https://webfinger.net/rel/</a>) It is not guaranteed to be HTML, but a <code>type</code> property can further define that. Per the definition, "it's likely text/html if it's for users."</p>
<p>An advantage of using Webfinger for discovery is that it is widely implemented by ActivityPub publishers to enable using <code>acct</code> URLs as identities.</p>
<section>
<h5>Examples</h5>
<p>
Given an ActivityPub <code>Place</code> object at <code>https://ap.example/geo/place-7.jsonld</code>, a consumer could use a Webfinger query to find the HTML page for the object:
</p>
<pre class="HTTP">
GET /.well-known/webfinger?resource=https%3A%2F%2Fap.example%2Fgeo%2Fplace-7.jsonld HTTP/1.1
Host: ap.example
</pre>
<p>Note that the <code>/.well-known/webfinger</code> path is fixed and required for Webfinger.</p>
<p>The publisher could return the following JRD output:</p>
<pre class="JSON">
{
"subject": "https://ap.example/geo/place-7.jsonld",
"links": [
{
"rel": "alternate",
"type": "text/html",
"href": "https://html.example/map/nl/ams/17921.html"
}
]
}
</pre>
<p>
In this example, the <code>links</code> property of the JRD object contains a single object with a <code>rel</code> property set to <code>alternate</code> and a <code>type</code> property set to <code>text/html</code>. The <code>href</code> property of this object is the URL of the HTML page representing the object.
</p>
<p>
Alternately, given an ActivityPub <code>Person</code> object at <code>https://ap.example/profiles/person-19.jsonld</code>, the consumer could construct an <code>acct</code> URL as <code>acct:[email protected]</code> and use it as the <code>resource</code> parameter for the Webfinger query:
</p>
<pre class="HTTP">
GET /.well-known/webfinger?resource=acct%3Aperson-19%40ap.example HTTP/1.1
Host: ap.example
</pre>
<p>Note that the <code>/.well-known/webfinger</code> path is fixed and required for Webfinger discovery.</p>
<p>The publisher could return the following JRD output:</p>
<pre class="JSON">
{
"subject": "acct:[email protected]",
"links": [
{
"rel": "http://webfinger.net/rel/profile-page",
"type": "text/html",
"href": "https://html.example/profiles/person-19.html"
}
]
}
</pre>
<p>In this output, the <code>http://webfinger.net/rel/profile-page</code> relationship identifies an HTML page for the <code>Person</code> object.</p>
</section>
<section>
<h5>Webfinger failure</h5>
<p>Some servers may not return JRD documents for <code>https</code> URLs. Others might only return JRD documents for URLs that represent actors, such as registered users.</p>
</section>
</section>
</section>
<section>
<h3>Document as input</h3>
<p>These techniques require the ActivityPub JSON-LD document as the input for the process. The document can be obtained through delivery via the ActivityPub protocol, or through the ActivityPub API, or by other means. </p>
<p>If none of these techniques are successful, the consumer can obtain the URL of the object from the <code>id</code> property, and then try one or more of the techniques that require an URL.</p>
<section>
<h4><code>url</code> property</h4>
<p>ActivityPub objects can have an optional <code>url</code> property, which "[i]dentifies one or more links to representations of the object." The property is the preferred way to indicate a corresponding HTML page for an ActivityPub object.
</p>
<p>As with many Activity Vocabulary properties, this can have several formats:
</p>
<ul>
<li>A string. In this case, the string is the URL itself.</li>
<li>A <code>Link</code> object. This structure is used to provide additional information about the link, including the <code>mediaType</code>. For an equivalent HTML representation, the <code>mediaType</code> property will be "text/html". The <code>href</code> property of the <code>Link</code> object is the URL.</li>
<li>An array: One or more strings and/or <code>Link</code> objects.</li>
</ul>
<section>
<h5>Examples</h5>
<p>In this example, the <code>url</code> property is only a string.</p>
<pre class="HTTP">
{
"@context": "https://www.w3.org/ns/activitystreams",
"id": "https://ap.example/some/path/person-1.jsonld",
"type": "Person",
"name": "Person One",
"url": "https://html.example/profile/person-1.html"
}
</pre>
<p>In the next example, the <code>url</code> property is a full <code>Link</code>-type object with <code>mediaType</code> property
equal to "text/html".</p>
<pre class="HTTP">
{
"@context": "https://www.w3.org/ns/activitystreams",
"id": "https://ap.example/geo/place-17.jsonld",
"type": "Place",
"nameMap": {
"en": "Berlin"
},
"url": {
"type": "Link",
"mediaType": "text/html",
"href": "https://html.example/map/de/ber/ber.html"
}
}
</pre>
<p>
In this final example, the <code>url</code> property is an array of <code>Link</code>-type objects with different <code>mediaType</code> properties.
</p>
<pre class="HTTP">
{
"@context": "https://www.w3.org/ns/activitystreams",
"id": "https://ap.example/photos/gallery/image-3.jsonld",
"type": "Image",
"summary": "Jason and Carol at the lake house",
"url": [
{
"type": "Link",
"mediaType": "text/html",
"href": "https://html.example/gallery/3.html"
},
{
"type": "Link",
"mediaType": "image/webp",
"href": "https://upload.example/files/08/17/2021/lakehouse.webp"
}
]
}
</pre>
</section>
<section>
<h5><code>url</code> property failure</h5>
<p>
When the <code>url</code> property is only a string, it may not
represent an HTML page. Especially for objects with binary content types, like <code>Image</code>, <code>Video</code>, and <code>Audio</code>, the <code>url</code> property is often used for the URL of the respective binary representation of the object.
</p>
<p>
The <code>mediaType</code> of <code>Link</code>-type objects in the <code>url</code> property is not always defined, and when it is defined, it is not always "text/html".
</p>
<p>
The property is defined only for representations of the current object. However, the <code>Link</code>-type object can have a link relation property, <code>rel</code>. Publishers may misuse the <code>url</code> property to including links that aren't a representation of the object, but instead a related object, like "next" or "author".
</p>
</section>
</section>
</section>
</section>
<section id="author-discovery">
<h2>HTML to author ActivityPub</h2>
<p>This section describes various methods for determining the ActivityPub ID of the author of an object represented by an HTML page.</p>
<section>
<h3>Discover equivalent object</h3>
<p>One way to discover the author of an object represented by an HTML page is to first discover the object's ActivityPub JSON-LD representation, then use that information to determine the author.</p>
<p>Any of the methods in the <a href="#discovery">forward discovery</a> section can be used to find the URL of the ActivityPub JSON-LD resource. Fetching the resource will retrieve the full JSON-LD representation of the object.</p>
<p>Three main properties are used, in ActivityPub, to identify the author of an object:</p>
<ul>
<li><a href="https://www.w3.org/TR/activitystreams-vocabulary/#dfn-attributedto">attributedTo</a>. This is the primary property for defining the author of an object. It is primarily used for content-type objects, such as <code>Note</code>, <code>Image</code> or <code>Video</code>, but can also be used for other object types.</li>
<li><a href="https://www.w3.org/TR/activitystreams-vocabulary/#dfn-actor">actor</a>. This identifies the actor who performed activity-type objects, such as a <code>Like</code> or <code>Question</code> activity.</li>
<li><a href="https://w3c-ccg.github.io/security-vocab/#owner">owner</a>. This property is almost exclusively used for <a href="https://w3c-ccg.github.io/security-vocab/#publicKey">publicKey</a> objects.</li>
</ul>
<p>Each of these properties can have values that are a string, a JSON object, or a JSON array consisting of strings and/or JSON objects. If the value is a string, it is the URL of the author object. If the value is an object, its <code>id</code> property is the URL of the ActivityPub JSON-LD object. For an array, each item can be resolved as either a string or an object.</p>
<p>The advantage of this method is that it does not require a separate discovery path for authors.</p>
<section>
<h4>Example</h4>
<p>Given an HTML page at <code>https://html.example/note-1.html</code> that represents a <code>Note</code> object, the consumer can use an HTML <link> header to identify the URL of the ActivityPub note:</p>
<pre class="HTML">
<link
rel="alternate"
type="application/activity+json"
href="https://ap.example/api/notes/note-1.jsonld" />
</pre>
<p>Fetching the content at <code>https://ap.example/api/notes/note-1.jsonld</code> will return the JSON-LD representation of the note:</p>
<pre class="JSON">
{
"@context": "https://www.w3.org/ns/activitystreams",
"id": "https://ap.example/api/notes/note-1.jsonld",
"type": "Note",
"content": "This is a note.",
"attributedTo": {
"id": "https://ap.example/profiles/person-1.jsonld",
"type": "Person",
"name": "Person One"
}
}
</pre>
<p>
In this example, the <code>attributedTo</code> property is an object with an <code>id</code> property that is the URL of the author object.
</p>
</section>
<section>
<h4>Object discovery failure</h4>
<p>This discovery method can fail if the ActivityPub object requires authentication to be fetched -- which is often the case for content that was not made available to the public by the author. A simple consumer may not have the context necessary to use OAuth 2.0, HTTP Signature, or other authentication methods.</p>
<p>Not all ActivityPub implementations include the <code>attributedTo</code> or <code>actor</code> property for an object in its default representation.</p>
</section>
</section>
<section>
<h3>URL as input</h3>
<p>This section describes techniques for discovering an ActivityPub author object using the HTML representation's URL as an input. The HTML representation's URL may come from ActivityPub properties, links in other content, or by other means.</p>
<p>If these techniques do not succeed, consumers can fetch the content of the HTML document and use one of the techniques that require HTML as input.</p>
<section>
<h4>HTTP Link header</h4>
<p>The <code>Link</code> header can also be used to identify the author of an HTML page. The <a href="https://html.spec.whatwg.org/multipage/links.html#link-type-author">author</a> link type identifies the author of a resource. So, a <code>Link</code> header with a <code>rel</code> property equal to "author" and a <code>type</code> property equal to "application/activity+json" will link to the ActivityPub representation of the author of the object.</p>
<p>This technique has the advantage of requiring a minimum amount of data transfer and complicated parsing of data structures. It can also be used with different media types than just HTML; for example, a JPEG image.</p>
<section>
<h5>Example</h5>
<p>
Given an HTML page at <code>https://html.example/files/video-33.html</code> that represents a <code>Video</code> object, the consumer can use an HTTP <code>HEAD</code> request to identify the URL of the ActivityPub video:
</p>
<pre class="HTTP">
HEAD /files/video-33.html HTTP/1.1
Host: html.example
</pre>
<p>The publisher would respond with the HTTP headers:</p>
<pre class="HTTP">
HTTP/1.1 200 OK
Content-Type: text/html
Link: <https://ap.example/api/videos/video-33.jsonld>; rel="alternate"; type="application/activity+json"
Link: <https://ap.example/profiles/person-7.jsonld>; rel="author"; type="application/activity+json"
</pre>
<p>Note that there are two <code>Link</code> headers in the result; one represents the <code>Video</code>, and the other represents the <code>Person</code> that authored the video. The <code>rel</code> value of "author" distinguishes the author link.</p>
</section>
<section>
<h5>Link header failure</h5>
<p>Some non-compliant publishers will respond to a <code>HEAD</code> request with the full body of the HTML document.</p>
</section>
</section>
</section>
<section>
<h3>Document as input</h3>
<p>These techniques require the contents of the HTML representation. This could already be available in a browser environment, or through other means.</p>
<p>If these techniques don't succeed, it's sometimes possible to obtain the URL of the document through the environment, for example, using the <code>document.location</code> property in a browser JavaScript program. Consumers can then try techniques that use the URL as input.</p>
<section>
<h4>HTML <link> element</h4>
<p>The ActivityPub id of the author of an object represented by an HTML document can also be found using the HTML <link> element. As with the HTTP <code>Link</code> header, if a <link> element has a <code>rel</code> attribute equal to "author" and a <code>type</code> attribute equal to "application/activity+json", its <code>href</code> property is the URL of the ActivityPub object representing the author.</p>
<p>This technique is useful when the HTML page is already downloaded and available, such as within a browser environment.</p>
<section>
<h5>Example</h5>
<p>Given an HTML page at <code>https://html.example/files/document-40.html</code> that represents a <code>Document</code> object, the consumer can use the following HTML to identify the URL of the ActivityPub representation of the document:</p>
<pre class="HTML">
<!doctype html>
<html>
<head>
<title>Document 33</title>
<link
rel="author"
type="application/activity+json"
href="https://ap.example/profiles/person-7.jsonld" />
</head>
<body>
<!-- rest of the page -->
</body>
</html>
</pre>
<p>In this example, the <link> element with the <code>rel</code> attribute set to "author" and the <code>type</code> attribute set to "application/activity+json" is the author of the document.</p>
</section>
<section>
<h5>Link element failure</h5>
<p>Some servers may include a <link> element with an <code>author</code> relation and with a JSON type or JSON-LD type that does not link to an ActivityPub resource.</p>
</section>
</section>
<section>
<h4>OpenGraph Protocol</h4>
<p>
The <a href="https://ogp.me/">OpenGraph Protocol</a> is a set of metadata properties that can be included in an HTML document to provide information about the document. Several of these properties can be used to identify the author of the document; usually with the URL of an HTML page that has a <a href="https://ogp.me/#type_profile">profile</a> type.
</p>
<ul>
<li><code>music:musician</code> identifies the creator of a song or album.</li>
<li><code>music:creator</code> identifies the creator of a playlist or station.</li>
<li><code>video:actor</code> identifies the actor in a video.</li>
<li><code>video:director</code> identifies the director of a video.</li>
<li><code>video:writer</code> identifies the writer of a video.</li>
<li><code>article:author</code> identifies the author of an article.</li>
<li><code>book:author</code> identifies the author of a book.</li>
</ul>
<p>
For these properties, the most likely discovery path is to follow the property's URL value to an HTML page for the profile, and then use <a href="#discovery">forward discovery</a> to find the ActivityPub object corresponding to that profile.
</p>
<p>
Another option is the <code>fediverse:creator</code> property, <a href="https://blog.joinmastodon.org/2024/07/highlighting-journalism-on-mastodon/">developed by Mastodon</a>. This property is used to identify the creator of a document. Its value is the Webfinger ID of the creator, prefixed with an <code>@</code> character, not an <code>acct:</code> URL. The consumer can use the Webfinger protocol to find the ActivityPub object corresponding to the creator.
</p>
<p>The advantage of using OGP for metadata is that it is widely implemented by Web publishing systems.</p>
<section>
<h5>Example</h5>
<p>
Given an HTML page at <code>https://html.example/files/article-40.html</code> that represents a <code>Article</code> object, the consumer can use the OpenGraph metadata to identify the URL of HTML profile for the author of the article:
</p>
<pre class="HTML">
<!doctype html>
<html>
<head>
<title>Article 40</title>
<meta property="article:author" content="https://html.example/profiles/person-7.html" />
</head>
<body>
<!-- rest of the page -->
</body>
</html>
</pre>
<p>
In this example, the <code>article:author</code> property is the author of the document. (Note that OpenGraph Protocol uses the non-standard <code>property</code> attribute for metadata.)
</p>
<p>
Given an HTML page at <code>https://html.example/files/video-40.html</code> that represents a <code>Video</code> object, the consumer can use OpenGraph metadata to identify the Webfinger ID of the creator of the video:
</p>
<pre class="HTML">
<!doctype html>
<html>
<head>
<title>Video 40</title>
<meta property="fediverse:creator" content="@[email protected]" />
</head>
<body>
<!-- rest of the page -->
</body>
</html>
</pre>
<p>
In this example, the <code>fediverse:creator</code> property is the Webfinger ID of the creator of the video, prefixed with the <code>@</code> symbol. The Webfinger ID can be used to find the ActivityPub object corresponding to the creator.
</p>
</section>
<section>
<h5>OpenGraph Protocol failure</h5>
<p>
The OpenGraph Protocol is not widely used for identifying authors of documents. When it is used, the profile page may not support discovery of an ActivityPub object.
</p>
</section>
</section>
</section>
<section>
<h3>Discovering author HTML</h3>
<p>Another option for author discovery is to discover the HTML profile page for the author of the resource, and then discover the equivalent ActivityPub object for the HTML profile page.</p>
<p>Techniques for discovering author HTML pages are out of scope for this report, but may include using HTTP <code>Link</code> headers, <link> elements, <a> elements, or Webfinger <code>link</code> objects with the <code>rel</code> property set to "author" and the <code>type</code> property set to "text/html" or omitted. Embedded JSON-LD, OpenGraph Protocol, and other mechanisms exist for discovering author profile HTML pages.</p>
<section>
<h4>Example</h4>
<p>Given the contents of an HTML page at <code>https://html.example/files/video-40.html</code> that represents a <code>Video</code> object, the consumer can check the <link> elements in the HTML to find the URL of the author's profile page:</p>
<pre class="HTML">
<!doctype html>
<html>
<head>
<title>Video 40</title>
<link
rel="author"
type="text/html"
href="https://html.example/profiles/person-22.html" />
</head>
<body>
<!-- rest of the page -->
</body>
</html>
</pre>
<p>It could then use a forward discovery technique, such as HTTP <code>Link</code> header discovery, to get the equivalent ActivityPub object.</p>
<pre class="HTTP">
HEAD /profiles/person-22.html HTTP/1.1
Host: html.example
</pre>
<p>The response might include a <code>Link</code> header with the URL of the equivalent ActivityPub object:</p>
<pre class="HTTP">
HTTP/1.1 200 OK
Content-Type: text/html
Link: <https://ap.example/profiles/person-22.jsonld>; rel="alternate"; type="application/activity+json"
</pre>
</section>
<section>
<h4>Author HTML failure</h4>
<p>Author HTML discovery can fail if the author's profile page is not available, or if the profile page does not link to an ActivityPub object.</p>
</section>
</section>