]> Lady’s Gitweb - Blog/blob - 2023-05-26/federated_tagging/#entry.rdf
Use data: u·r·i’s for branding
[Blog] / 2023-05-26 / federated_tagging / #entry.rdf
1 <awol:Entry
2 xml:lang="en"
3 xmlns:awol="http://bblfish.net/work/atom-owl/2006-06-06/"
4 xmlns:dc11="http://purl.org/dc/elements/1.1/"
5 xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
6 xmlns:sioc="http://rdfs.org/sioc/ns#"
7 >
8 <dc11:title>Federated Tagging of Fanworks</dc11:title>
9 <dc11:date>2023-05-26T17:27:26-07:00</dc11:date>
10 <dc11:abstract rdf:parseType="Markdown"><![CDATA[
11 Fans like to use <cite>A·O·3</cite>’s tagging system as an excuse for
12 why decentralization is impossible. But actually, there’s no reason why
13 having a centralized tag authority should require centralization in
14 fannish repositories as well.
15 ]]></dc11:abstract>
16 <sioc:content rdf:parseType="Markdown"><![CDATA[
17 As [recent conversations regarding a·i][satsuma:25796] and [racism in
18 fandom][end_otw_racism] continue to exert pressure on the idea that the
19 [Organization for Transformative Works][OTW] is in any way a good
20 steward of the 11 million fanworks on their website [<cite>Archive of
21 Our Own</cite>][AO3], some fans are again beginning to wonder if a
22 centralized repository controlled by a single organization is truly the
23 best solution for a community as diverse as fandom.
24
25 There are a few reasons why one might say it is, but in this post I
26 want to confront one: tagging. Tags (short labels which have been
27 “wrangled” and organized into a hierarchial concept scheme) are the
28 life·blood of search, filtering, and discovery on <cite>A·O·3</cite>,
29 and powering this system is a huge cadre of volunteers whose duty it is
30 to ensure that each label an author assigns to their work is organized
31 correctly. <cite>A·O·3</cite>’s tagging system isn’t perfect—far from
32 it—but the fact remains that a small, independent fan repository is
33 unlikely to ever be able to muster the level of volunteer effort needed
34 to build a tagging system of comparable utility. So, the argument goes,
35 posting works to <cite>A·O·3</cite> is, at the very least, a necessary
36 evil.
37
38 I think this argument suffers from a failure of imagination. Even if we
39 agree on the premise that tag wrangling work is labour‐intensive and
40 fannish needs are best accommodated by a large, centralized tag
41 authority, there is absolutely no reason why that tag authority
42 couldn’t be shared by multiple small, independent repositories instead
43 of being entirely controlled by one big one. We just need a way to get
44 the tags from point A to point B.
45
46 This blogpost describes one such way. It uses [the <cite>Tagging</cite>
47 vocabulary][Tagging] to describe and federate tags from one centralized
48 authority—which could be a large, volunteer‐run tag‐wrangling service
49 in the model of <cite>A·O·3</cite>—out to potentially infinite
50 independent repositories. It’s not necessarily a complete solution, and
51 it’s unlikely that a system built with these technologies would work
52 exactly like things do at <cite>A·O·3</cite>—but this is not
53 necessarily a bad thing.
54
55 ## Tag Model
56
57 The <cite>Tagging</cite> vocabulary is an R·D·F ontology, which is to
58 say it is a collection of terms and definitions which might be used to
59 convey information between computers on the Web. It’s not a syntax or
60 a protocol, although the conventions of the present day dictate that
61 this information should probably be conveyed in [J·son‐L·D][JSON-LD]
62 via normal H·T·T·P·S `GET` requests. A tag might look as follows :—
63
64 ```json
65 { "@context": "https://ns.1024.gdn/Tagging/context.jsonld"
66 , "@id": "https://ladys.example/tag:ladys.example,2023-05-23:5NJ-8SKG"
67 , "@type": "SexualRelationshipTag"
68 , "prefLabel":
69 { "literalForm":
70 { "@value": "Shadow × O·C"
71 , "@language": "en" } }
72 , "altLabel":
73 { "literalForm":
74 { "@value": "Sexual Relationship: Shadow the Hedgehog & Original Character"
75 , "@language": "en" } }
76 , "involves":
77 [ "https://ladys.example/tag:ladys.example,2023-05-23:N54-M773"
78 , "https://ladys.example/tag:ladys.example,2023-05-23:V2G-36D8" ] }
79 ```
80
81 This object describes the tag
82 `tag:ladys.example,2023-05-23:5NJ-8SKG`—or, as humans might refer to
83 it, ‹ Shadow × O·C ›. It is a sexual relationship tag involving two
84 other tags (`tag:ladys.example,2023-05-23:N54-M773` and
85 `tag:ladys.example,2023-05-23:V2G-36D8`—presumably ‹ Shadow › and
86 ‹ Original Character ›), and it has both a preferred label and an
87 alternate one provided. If `ladys.example` were a real tag authority
88 and this were a real tag, any repository would be able to fetch this
89 U·R·L and retrieve the tag, its label, and its relationships.
90
91 If <cite>A·O·3</cite> provided this kind of service, we could begin
92 building fanfiction repositories which leveraged their tagging system
93 *today*.
94
95 ## Works and Tag Labels
96
97 Of course, simply having dereferencable tags on the internet is only
98 half of the struggle. We also need a means of associating them with
99 works. In the <cite>Tagging</cite> vocabulary, this happens through a
100 layer of indirection, because the labels that authors provide often do
101 not match up exactly with the tag authority’s preferred labels for the
102 tag. The way this works is as follows :—
103
104 - The work itself *has a tag label collection*, probably an ordered
105 one.
106
107 - This tag label collection then *contains tag labels*, which provide
108 the author‐assigned labels attached to the work.
109
110 - Labels may then *label* a specific tag, linking to the formal
111 definition of the tag in some authority.
112
113 How this all gets actually managed in practice is up to the repository
114 in question; it’s certainly possible to use tags with a simpler (or
115 more complex) model than this, and questions of how works *themselves*
116 might get federated is a bit out·of·scope for this discussion. However,
117 my own immediate target is building an [Atom feed][RFC4287] of
118 fanfiction I’ve written; in this case, the tags might be added using
119 [AtomTriples][draft-nottingham-atomtriples-00] like so :—
120
121 ```xml
122 <feed
123 xmlns="http://www.w3.org/2005/Atom"
124 xmlns:at="http://purl.org/syndication/atomtriples/1"
125 xmlns:html="http://www.w3.org/1999/xhtml"
126 xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
127 xmlns:t="https://ns.1024.gdn/Tagging/#"
128 xmlns:xl="http://www.w3.org/2008/05/skos-xl#"
129 xml:lang="en"
130 >
131 <title>Lady’s Fanfiction</title>
132 <link rel="alternate" type="text/html" href="https://fanfiction.ladys.example/"/>
133 <updated>2023-05-23T12:53:37-07:00</updated>
134 <author>
135 <name>Lady</name>
136 </author>
137 <id>https://fanfiction.ladys.example/tag:fanfiction.ladys.example,2023-05-23:</id>
138 <entry>
139 <title>fucky wucky shadow daddy dom me</title>
140 <link rel="alternate" type="text/html" href="https://fanfiction.ladys.example/tag:fanfiction.ladys.example,2023-05-23:EQW-D8HX"/>
141 <id>https://fanfiction.ladys.example/tag:fanfiction.ladys.example,2023-05-23:EQW-D8HX</id>
142 <updated>2023-05-23T12:53:37-07:00</updated>
143 <summary type="text">
144 big bad shadow the hedgehog thinks hes the ultimate lifeform. but
145 hes actually kind of short. can he still dom a hot dyke like me?
146 </summary>
147 <at:md>
148 <!-- The tag label collection for this entry -->
149 <t:hasTagLabelCollection rdf:parseType="Resource">
150 <t:hasTagLabelList rdf:parseType="Collection">
151 <t:TagLabel xl:literalForm="fuuck me shaddw">
152 <t:labels>
153 <!-- Shadow × Original Character -->
154 <rdf:Description rdf:resource="https://ladys.example/tag:ladys.example,2023-05-23:5NJ-8SKG"/>
155 </t:labels>
156 </t:TagLabel>
157 <t:TagLabel xl:literalForm="daddy th ehedgeog">
158 <t:labels>
159 <!-- Shadow -->
160 <rdf:Description rdf:resource="https://ladys.example/tag:ladys.example,2023-05-23:N54-M773"/>
161 </t:labels>
162 </t:TagLabel>
163 <t:TagLabel xl:literalForm="me">
164 <t:labels>
165 <!-- Original Character -->
166 <rdf:Description rdf:resource="https://ladys.example/tag:ladys.example,2023-05-23:V2G-36D8"/>
167 </t:labels>
168 </t:TagLabel>
169 </t:hasTagLabelList>
170 </t:hasTagLabelCollection>
171 </at:md>
172 </entry>
173 </feed>
174 ```
175
176 Note how each `<t:TagLabel>` above has an `@xl:literalForm` which
177 supplies the author‐provided tag label, while also labelling an R·D·F
178 resource which gives the canonical tag. This is obviously quite verbose
179 (by nature of being X·M·L), and more sophisticated solutions will
180 doubtless be developed in the future, but it is already good enough to
181 start building things on top of.
182
183 ## Pulling in Tags
184
185 Simply having the ability to fetch tags on request is likely
186 insufficient for complex tagging systems or large repositories. A tag
187 authority could have thousands of tags, and a repository needs to be
188 able to not only discover them all but also keep them up·to·date
189 without individually fetching each one. Inspired by the [<cite>I·I·I·F
190 Change Discovery A·P·I</cite>][IIIF-Discovery], the current plan is to
191 provide tag authorities with an [ActivityStreams][] event log which
192 chronicles their changes, which repositories can then pull to update
193 their models. The creation of a new romantic relationship tag
194 `tag:ladys.example,2023-05-23:$BF-7607` might look like this :—
195
196 ```json
197 { "@context": "https://ns.1024.gdn/Tagging/discovery.context.jsonld"
198 , "type":
199 [ "TagActivity"
200 , "Create" ]
201 , "context": "https://ladys.example/tag:ladys.example,2023-05-23:"
202 , "object": "https://ladys.example/tag:ladys.example,2023-05-23:$BF-7607"
203 , "endTime": "2023-05-23T19:33:33-07:00"
204 , "label": "Add Shadow ❤ O·C tag"
205 , "comment": "This is the romantic equivalent of the existing (sexual) Shadow × O·C tag."
206 , "states":
207 [ { "predicate": "a"
208 , "object": "RomanticRelationshipTag" }
209 , { "predicate": "prefLabel"
210 , "object":
211 { "@value": "Shadow ❤ O·C"
212 , "@language": "en" } }
213 , { "predicate": "involves"
214 , "object": "https://ladys.example/tag:ladys.example,2023-05-23:N54-M773" }
215 , { "predicate": "involves"
216 , "object": "https://ladys.example/tag:ladys.example,2023-05-23:V2G-36D8" } ] }
217 ```
218
219 A change in the preferred label of that tag might look as follows :—
220
221 ```json
222 { "@context": "https://ns.1024.gdn/Tagging/discovery.context.jsonld"
223 , "type":
224 [ "TagActivity"
225 , "Update" ]
226 , "context": "https://ladys.example/tag:ladys.example,2023-05-23:"
227 , "object": "https://ladys.example/tag:ladys.example,2023-05-23:$BF-7607"
228 , "endTime": "2023-05-23T19:34:20-07:00"
229 , "label": "Add variation selector 15 after heart"
230 , "comment": "Helps to prevent emojification on mobile platforms."
231 , "unstates":
232 [ { "predicate": "prefLabel"
233 , "object":
234 { "@value": "Shadow ❤ O·C"
235 , "@language": "en" } } ]
236 , "states":
237 [ { "predicate": "prefLabel"
238 , "object":
239 { "@value": "Shadow ❤\uFE0E O·C"
240 , "@language": "en" } } ] }
241 ```
242
243 In each case note that the activity `unstates` and `states` various
244 triples, with the `subject` of each assumed to be the same as the
245 `object` of the activity. As these are published in an chronological,
246 paginated list, it is easy for consumers to skip activities that they
247 have seen before. And because each activity identifies as its `object`
248 the tag being changed, they can easily skip activities for tags they
249 don’t care about, too.
250
251 ## Implementation Plans
252
253 I’m currently working on a library for processing tag streams and a
254 command‐line tool for generating them. Because this is a pull‐based
255 model, it should be pretty easy to get a proof‐of‐concept up that is
256 backed by static files and a minimal Caddy configuration. Then I will
257 attempt to create a simple web viewer for browsing the tags, as well as
258 start publishing works which use them. I’ll be sure to post updates to
259 this blog when·ever any of this happens!
260
261 A lot of the initial discussion for this work has taken place on the
262 [Fandom Coders][Fancoders] Discord server. Shoutout to the people there
263 for putting up with my many 4 A·M Freeform diagrams and rants about
264 ActivityStreams.
265
266 [AO3]: <https://archiveofourown.org/> "Archive of Our Own"
267 [ActivityStreams]: <https://www.w3.org/TR/activitystreams-core/> "ActivityStreams 2.0"
268 [Fancoders]: <https://www.fancoders.com> "Fandom Coders"
269 [IIIF-Discovery]: <https://iiif.io/api/discovery/1.0/> "IIIF Change Discovery API 1.0"
270 [JSON-LD]: <https://json-ld.org> "JSON‐LD: JSON for Linking Data"
271 [OTW]: <https://www.transformativeworks.org/> "Organization for Transformative Works"
272 [RFC4287]: <https://www.rfc-editor.org/rfc/rfc4287.html> "The Atom Syndication Format"
273 [Tagging]: <https://ns.1024.gdn/Tagging/> "The Tagging Vocabulary"
274 [draft-nottingham-atomtriples-00]: <https://datatracker.ietf.org/doc/html/draft-nottingham-atomtriples-00> "AtomTriples: Embedding RDF Statements in Atom"
275 [end_otw_racism]: <https://blog.ladys.computer/2023-05-14/end_otw_racism/> "End Racism in the O·T·W"
276 [satsuma:25796]: <https://satsuma.dreamwidth.org/25796.html> "A Chronic Habit of Avoiding Responsibility? #EndOTWRacism"
277 ]]></sioc:content>
278 <dc11:rights rdf:parseType="Markdown"><![CDATA[
279 Copyright © 2023
280 <a href="https://www.ladys.computer/about/#lady">Lady</a>
281 <small>[ActivityStreams Hatefucker]</small>.
282 Some rights reserved.
283
284 This blogpost is licensed under a <a rel="license"
285 href="http://creativecommons.org/licenses/by/4.0/"><cite>Creative
286 Commons Attribution 4.0 International License</cite></a>.
287 ]]></dc11:rights>
288 </awol:Entry>
This page took 0.075117 seconds and 5 git commands to generate.