(Still a draft)
Open Glosses is a lightweight architecture for open content annotation. It relies heavily on existing web standards but is designed to be versatile and scalable. I built it by taking beingmeta’s “gloss database” for storing glosses (notes, tags, links, etc) on sBooks and trying to come up with a system where beingmeta could still compete but not have a monopoly. Open glosses is the result.
A gloss is an annotation attached to a fine-grained web reference, typically below the level of a single document, for example a paragraph, quote, list item, etc. A gloss consists of a fine-grained reference (or multiple reference paths) to a ‘point’ in a web document. The reference is accompanied by a textual comment, creation and modification dates, an optional excerpt (encoded by both content and text range). It can also include any number of tags, links (URL references with titles), and outlets. An outlet is a tag with permissions: outlets are limited by who can assign them, see them, or search based on them.
The protocol for glosses is based around an HTTP(S) endpoint called a knotary; a given annotation consists of the endpoint followed by a UUID unique identifying the endpoint. For example, if the endpoint were https://www.archive.org/pubglosses/, a reference to an annotation might be https://archive.org/pubglosses/ab857636-ed6b-11e0-8515-040ccedb18d8.
In our current implementations and in this post, we’ll assume that the annotations themselves are represented using JSON. In a ‘grown-up’ implementation, it would presumably respond responsibly to HTTP ‘Accepts’ headers, but our implementation is still in middle school at this point.
Every annotation has a ‘refuri’ field and a ‘frag’ field identifying a web document and a passage within that document. The frag field can be an XML ID or it can use a different addressing scheme (see my post on How to Point To A Paragraph). We introduce the convention that a frag of the form:
#_scheme_schemedata
indicates a reference using a particular scheme, so that:
#_MD5WSN_xxxxx
references the passage whose MD5 hash of its “word segmented normalization” is xxxxxx.
In addition, a gloss can have a separate alturis field giving other URIs which refer to the same document and an altfrags field giving other fragment identifiers which refer to the same passage. Note that the frag reference need not be an actual ID, but coould be a derived reference as described above.
Finally, the gloss can include an altrefs fields whose URLs take the form url#FRAG and refer to the same passage. By implication, the altrefs include refuri#frag as well as the cross product of alturis and altfrags. Got that? (The main idea is that a gloss may have lots of different references to the passage it annotates).
glosses have a ‘note’ field for a comment (UTF-8, no formatting). They can also have an ‘excerpt’ field consisting of a string. Some implementations or content sources may choose to limit the size of the excerpt for intellectual property reasons. There are a handful of additional fields which I’ll describe shortly. But back to the endpoint and its semantics, which are based on HTTP.
The protocol uses HTTP GET/PUT/POST to access, create, and modify annotations (glosses). To create a new annotation, you just POST (with a JSON payload, other options in the grown-up version) to the endpoint+UUID and an annotation is created; to modify the annotation, you do a PUT on the endpoint+UUID.
GET, of course, just gets the (currently JSON) representation of an annotation given the UUID. Ideally, the endpoint honors HTTP headers like Modified-Since and whatever security promises are made by the endpoint.
To talk about the query mode, we need to introduce the extended but (still) relatively minimal field set for the model.
In addition to refuri, refuris, frag, frags, altrefs, and text, glosses always have:
- maker: who created the annotation; this can be a user object reference relative to some domain/database (that’s what sBooks uses), an email address or uri (ideally verified), or even a public key (for stable anonymous annotation);
- created: the date/time when the annotation was created;
- modified: the date/time which the annotation was last modified;
and optionally,
- signature: a computed value proving that the maker really created this particular annotation
As mentioned above, glosses have a text field which is a (typically short) comment associated with the annotation. They also have an (optional) excerpt and (optional) content field, together with tags, links, and outlets.
- The content field has more detailed content and the content_type field gives it’s mime type, which defaults to text/html.
- The tags field consists of tags associated with an annotation; we think it will be common to have annotations which are nothing but tags, providing alternative indices or orgnaizational schemes to existing content; tags are actually little knowledge base nodes using the knodules (www.knodules.org) syntax, which lets you specify terms, synonyms, relations, and all sorts of other cool relations.
- The outlets field determines who can see a given gloss but the details are determined by the endpoint implementation. The main points are: only certain people can add a given outlet to an annotation; only certain people can see that a post is associated with a particular outlet; and only certain people can find all glosses associated with a particular outlet.
- The links field is a collection of URLs and titles associated with a gloss.
The query mode allows applications or users to find glosses with particular properties. A query reference has the form:
endpoint?refuri=xxx&frag=xxx&tag=ttt
Many endpoints may require a refuri and a query may provide also multiple refuris for cases where a document may have many editions. Refuri parameters that include hashids are taken as direct references, while others are automatically combined with any frag parameters to find matching glosses. Queries can provide parameters like maker and outlet and the endpoint is expected to implement whatever semantics they’ve promised.
In addition, the query endpoint should at least handle the AFTER parameter to get glosses which have been created or modified after a particular time.
Over the past few weeks, I’ve had many friends and colleagues ask about using Apple’s new