3 A minimal git‐based microblog.
 
   7  +  A Python script, `post-receive`, suitable for use as a post‐receive
 
   8       Git hook, which generates a number of `.jsonld` files from a
 
   9       source repository of text posts.
 
  11  +  A small collection of H·T·M·L files designed to display the
 
  12       generated `.jsonld` in a human‐readable fashion.
 
  16  +  A server or server configuration (you will need to supply these
 
  19  +  Suitable for deployment on a simple filesystem server like
 
  20       NeoCities or GitHub Pages (it requires both Git hooks and H·T·T·P
 
  23  +  Interactive in any way (i·e “social media”).
 
  25  +  Compatible with the ActivityPub fediverse (or any other push‐based
 
  26       social media platform).
 
  28 ## Script Configuration
 
  30 You will need to edit the `post-receive` script to adjust the following
 
  31   constants to match your setup :—
 
  33  +  **`GIT_DIRECTORY`**:
 
  34     The absolute path to this Git repository on your server.
 
  35     It is expected that this will be a bare repository (ending in
 
  38  +  **`BUILD_DIRECTORY`**:
 
  39     In order to access the files when you push, the repository will be
 
  41     **This directory will be deleted on every push.**
 
  43  +  **`PUBLIC_DIRECTORY`**:
 
  44     The directory that your server serves files from.
 
  45     **This directory will be deleted on every push,** so if you need to
 
  46       serve additional files (i·e those not generated by this script),
 
  47       you should place those files in a different directory and adjust
 
  48       your server configuration accordingly.
 
  49     Note that the `post-receive` script and associted H·T·M·L files
 
  50       provided by this repository expect a certain server configuration
 
  54      The U·R·L that you are serving your site from (with no trailing
 
  56      You cannot serve Status.git from a subdirectory.
 
  58 A `Makefile` is provided to make installing the `post-receive` script
 
  59   on your server easy (assuming you have `ssh` access).
 
  60 You’ll need to supply some variables there, too :—
 
  66     The domain or other address of your site.
 
  68  +  **`GIT_REPOSITORY`**:
 
  69     The absolute path to this Git repository, as above.
 
  71 ## Server Configuration
 
  73 Your server should be configured to serve the following files from the
 
  74   provided `PUBLIC_DIRECTORY` in response to the following requests.
 
  75 For people using Caddy to serve their content, a sample `Caddyfile` is
 
  76   included in this repository.
 
  80 These responses **must** be served with a `Content-Type` of
 
  81   `text/html;charset=UTF-8` (or equivalent).
 
  82 Note that these paths **do not** have a trailing slash.
 
  85     Serve the file at `/index.html`.
 
  86     A `Link` header with the value
 
  87       `</about.jsonld>;rel=meta;type="application/ld+json"` (or
 
  88       equivalent) **must** be provided.
 
  91     Serve the file at `/.about.html`.
 
  92     A `Link` header with the value
 
  93       `</about.jsonld>;rel=meta;type="application/ld+json"` (or
 
  94       equivalent) **must** be provided.
 
  96  +  **`GET /statuses`**:
 
  97     Serve the file at `/.statuses.html`.
 
  98     A `Link` header with the value
 
  99       `</statuses.jsonld>;rel=meta;type="application/ld+json"` (or
 
 100       equivalent) **must** be provided.
 
 102  +  **`GET /statuses/$YYYY-MM`** (where `$YYYY-MM` is an `xsd:gYearMonth`):
 
 103     Serve the file at `/.topic.html`.
 
 104     A `Link` header with the value
 
 105       `</$YYYY-MM.jsonld>;rel=meta;type="application/ld+json"` (or
 
 106       equivalent) **must** be provided.
 
 108  +  **`GET /statuses/$YYYY-MM/*`** (where `$YYYY-MM` is an `xsd:gYearMonth`):
 
 109     Serve the file at `/.status.html`.
 
 110     A `Link` header with the value
 
 111       `</$YYYY-MM.jsonld>;rel=meta;type="application/ld+json"` (or
 
 112       equivalent) **must** be provided.
 
 115     Serve the file at `/.topics.html`.
 
 116     A `Link` header with the value
 
 117       `</topics.jsonld>;rel=meta;type="application/ld+json"` (or
 
 118       equivalent) **must** be provided.
 
 120  +  **`GET /topics/$TOPIC`** (where `$TOPIC` matches `[0-9A-Za-z_-]+`):
 
 121     Serve the file at `/.topic.html`.
 
 122     A `Link` header with the value
 
 123       `</$TOPIC.jsonld>;rel=meta;type="application/ld+json"` (or
 
 124       equivalent) **must** be provided.
 
 126  +  **`GET /topics/$TOPIC/*`** (where `$TOPIC` matches
 
 128     Serve the file at `/.status.html`.
 
 129     A `Link` header with the value
 
 130       `</$TOPIC.jsonld>;rel=meta;type="application/ld+json"` (or
 
 131       equivalent) **must** be provided.
 
 133 ### X·H·T·M·L responses
 
 135 These responses **must** be served with a `Content-Type` of
 
 136   `application/xhtml+xml` (or equivalent).
 
 137 Note that these paths **do not** have a trailing slash.
 
 139  +  **`GET /$IRI`** (where `$IRI` contains a colon and no slash):
 
 140     Serve the file at `/.lookup.xhtml`.
 
 141     This can be used to look up statuses by their identifier.
 
 143 ### Json‐L·D responses
 
 145 These responses **should** be served with a `Content-Type` of
 
 146   `application/ld+json`.
 
 147 In all cases, for `/$PATH.jsonld`, this just serves the file at
 
 148   `/$PATH/index.jsonld`.
 
 150  +  **`GET /about.jsonld`**:
 
 151     Serve the file at `/about/index.jsonld`.
 
 153  +  **`GET /statuses.jsonld`**:
 
 154     Serve the file at `/statuses/index.jsonld`.
 
 156  +  **`GET /statuses/$YYYY-MM.jsonld`** (where `$YYYY-MM` is an
 
 158     Serve the file at `/$YYYY-MM/index.jsonld`.
 
 160  +  **`GET /topics.jsonld`**:
 
 161     Serve the file at `/topics/index.jsonld`.
 
 163  +  **`GET /topics/$TOPIC.jsonld`** (where `$TOPIC` matches
 
 165     Serve the file at `/topics/$TOPIC/index.jsonld`.
 
 169 These responses **should** be served with a `Content-Type` of
 
 170   `application/atom+xml`.
 
 171 In all cases, for `/$PATH.atom`, this just serves the file at
 
 174  +  **`GET /statuses.atom`**:
 
 175     Serve the file at `/statuses/index.atom`.
 
 177  +  **`GET /statuses/$YYYY-MM.atom`** (where `$YYYY-MM` is an
 
 179     Serve the file at `/$YYYY-MM/index.atom`.
 
 181  +  **`GET /topics/$TOPIC.atom`** (where `$TOPIC` matches
 
 183     Serve the file at `/topics/$TOPIC/index.atom`.
 
 187 All responses **should** have a `Access-Control-Allow-Origin` header
 
 188   with a value of `*` (assuming your server does not use credentials
 
 189   and is not being served behind a firewall).
 
 191 ## Committing Statuses To Git
 
 193 The `post-receive` script will run whenever you make a commit to the
 
 194   `live` branch, which should be set as your default.
 
 195 Statuses are represented by a small collection of files committed to
 
 196   particular locations in this repository :—
 
 198  +  Files committed to `/YYYY/MM/DD/HH.MM.SSZ/` (the final component
 
 199       can actually take any form but a time is **recommended**) are
 
 202  +  Files committed to `/topic/TOPICNAME/a/b/c/d/xxxx` (where `a`, `b`,
 
 203       `c`, and `d` are lowercase hexadecimal digits and `xxxx` can be
 
 204       anything) is a status posted to a specific topic (`TOPICNAME`).
 
 205     It is **recommended** that you make `a/b/c/d` the first four digits
 
 206       of an MD5 hash of the status content and `xxxx` the remaining
 
 207       digits (security is not an issue here, so MD5 is fine).
 
 208     `TOPICNAME` should have the form `[0-9A-Za-z_-]+`.
 
 210 The intent is that “ordinary statuses” are a bit more ephemeral whereas
 
 211   “topic statuses” can serve as reference material.
 
 212 Using an MD5 hash for topic statuses ensures you don’t post the same
 
 215 The files which represent a status are as follows :—
 
 218     The text of the status.
 
 219     Blank lines separate paragraphs; linebreaks will be preserved.
 
 220     There is a special markup for links: `<https://link.example>`, or
 
 221       `<https://link.example>="link text"` if you want to supply link
 
 223     At present, no other markup is supported.
 
 225  +  **`0=x_status_git_1.0`**:
 
 226     This file is **optional** and not currently used for anything, but
 
 227       indicates that the post follows the `1.0` format.
 
 228     The contents of this file, if present, **must** be
 
 229       `x_status_git_1.0`, optionally followed by a trailing newline.
 
 231  +  **`1=NAME`** (where `NAME` might be anything):
 
 232     This file is **recommended** and indicates the author of the
 
 234     Only one author is currently supported.
 
 235     The value of `NAME` **must** give the name of the author of the
 
 237     The contents of this file **must** give the author’s URL,
 
 238       optionally followed by a trailing newline.
 
 240  +  **`2=TITLE`** (where `TITLE` might be anything):
 
 241     This file is **optional** and indicates the title of the status.
 
 242     The value of `TITLE` **should** be a file·system‐friendly version
 
 243       of the title, but is ignored.
 
 244     The contents of this file **must** give the title of the status,
 
 245       optionally followed by a trailing newline.
 
 247  +  **`3=YYYY-MM-DD`** (where `YYYY-MM-DD` is a date):
 
 248     This file is **required** and indicates the date of the status.
 
 249     Only one date is currently supported.
 
 250     The value of `YYYY-MM-DD` **should** give the date of the status,
 
 251       although this is not used (in favour of the full timestamp).
 
 252     The contents of this file **must** give the full `xsd:dateTime`
 
 253       timestamp of the status, optionally followed by a trailing
 
 256  +  **`4=IDENTIFIER`** (where `IDENTIFIER` might be anything):
 
 257     This file is **required** and provides an identifier for the
 
 259     Only one identifier is currently supported.
 
 260     The contents of this file **must** be an I·R·I which uniquely
 
 261       identifies the status (for example, a U·U·I·D or `tag:` URI).
 
 262     The value of `IDENTIFIER` **must** be a locally‐unique identifier
 
 263       for the status and **should** resemble the contents where
 
 265     (Note, however, that `IDENTIFIER` cannot contain slashes and need
 
 266       not be a valid I·R·I.)
 
 268 Files with names that begin with the strings `2=` or `x_status_git_`
 
 269   are reserved for backwards‐compatible extensions.
 
 271 x_status_git has no opinion on how these files make their way into the
 
 272   Git repository, except that all the files for a single status should
 
 273   be added in the same commit.
 
 274 The intention is that the simple nature of these files will make them
 
 277 ## I Am Computer, How Do I Get Status?
 
 279 Assume you are given a U·R·L `resource_url` which you think points to
 
 280   some kind of x_status_git resource.
 
 281 Start by resolving it as follows :—
 
 283 01. Make a `HEAD` request to `resource_url`.
 
 285 02. If there is a `Link` header with a `rel` of `meta` and a `type` of
 
 286       `application/ld+json`, set `resource_url` to the URL provided in
 
 287       that header and restart these steps from step 1.
 
 289 03. Make a `GET` request to `resource_url` and let `response` be the
 
 292 04. Set `document` as follows :—
 
 294     01. If the `Content-Type` header of `response` has a type of `text`
 
 295           and subtype of `html`, let `document` be the result of
 
 296           processing the body of `response` into a D·O·M tree as an
 
 298         It is an error if this process fails.
 
 300     02. If the `Content-Type` header of `response` has a type of
 
 301           `application` and a subtype which is `xml` or which ends in
 
 302           `+xml`, let `document` be the result of processing the body
 
 303           of `response` into a D·O·M tree as an X·M·L document .
 
 304         It is an error if this process fails.
 
 306     03. Otherwise, let `document` be null.
 
 308 05. If `document` is not null :—
 
 310     01. If there is a `<link>` element in either the H·T·M·L namespace
 
 311           or the Atom namespace in `document` with a `rel` of `meta`
 
 312           and a `type` of `application/ld+json`, set `resource_url` to
 
 313           the `href` of that `<link>` element and restart these steps
 
 315         If multiple such elements exist, choose the first one.
 
 317     02. Otherwise, it is an error.
 
 319 06. If the body of `response` is not a Json document, it is an error.
 
 321 Assuming the U·R·L you were given was valid, you will end this
 
 322   algorithm with a Json‐L·D response, and you can use the `@type`
 
 323   attribute to determine the response type.
 
 324 `@type` will be either a string or an array.
 
 326  +  If the `@type` is or contains `Forum`, the resource is a collection
 
 329  +  If the `@type` is or contains `Thread`, the resource is a
 
 330       collection of statuses.
 
 332  +  If the `@type` is or contains `Microblog`, the resource describes
 
 333       this site as a whole.
 
 334     The `streams` property will contain a list of available `Forum`s
 
 335       and `Thread`s, as objects with an `@id` and `@type`.
 
 337 The items in the collection (`Forum` or `Thread`) may be determined
 
 338   through one of the following methods :—
 
 340  +  If the `@type` is or contains `CollectionPage` or
 
 341       `OrderedCollectionPage`, then its `items` will contain resources.
 
 342     This is a partial collection, and the `prev` and `next` properties
 
 343       can be used to access further items from the parent collection
 
 344       (indicated by `partOf`).
 
 345     `first` and `current`, in this scenario, point “horizontally” to
 
 346       the first and latest pages of items, not to subpages.
 
 348  +  If the `@type` is or contains `Collection` or `OrderedCollection`
 
 349       and the resource has `first` and/or `current` properties, then
 
 350       the `items` property will not be present.
 
 351     `first` and `current` can be accessed to provide
 
 352       `OrderedCollectionPage`s listing the items of the collection.
 
 354  +  Otherwise, the `items` property will contain every item in the
 
 357 Statuses themselves have the following properties :—
 
 360     The identifier of the status.
 
 363     The value `MicroblogPost`.
 
 365  +  **`created`** [`dcterms:created`]:
 
 366     The creation date for the status, as an `xsd:dateTime`.
 
 368  +  **`creator`** [`dcterms:creator`] (optional):
 
 369     The author of the status, as an object with an `@id` and `name`
 
 372  +  **`identifier`** [`dcterms:identifier`]:
 
 373     An I·R·I which uniquely identifies the status.
 
 374     This differs from the `@id` in that it is not expected to be
 
 377  +  **`subject`** [`dcterms:subject`] (optional):
 
 378     The topic of the status, for topic statuses.
 
 380  +  **`title`** [`dcterms:title`] (optional):
 
 381     The title of the status.
 
 383  +  **`content`** [`sioc:content`]:
 
 384     The content of the status, as an `rdf:XMLLiteral`.