# Status.git

A minimal git‐based self‐hosted status publishing solution.

## What It Is

 +  A Python script, `post-receive`, suitable for use as a post‐receive
      Git hook, which generates a number of `.jsonld` files from a
      source repository of text posts.

 +  A small collection of H·T·M·L files designed to display the
      generated `.jsonld` in a human‐readable fashion.

## What It Is Not

 +  A server or server configuration (you will need to supply these
      yourself; see below).

 +  Suitable for deployment on a simple filesystem server like
      NeoCities or GitHub Pages (it requires both Git hooks and H·T·T·P
      routing).

 +  Interactive in any way (i·e “social media”).

 +  Compatible with the ActivityPub fediverse (or any other push‐based
      social media platform).

## Script Configuration

You will need to edit the `post-receive` script to adjust the following
  constants to match your setup :—

 +  **`GIT_DIRECTORY`**:
    The absolute path to this Git repository on your server.
    It is expected that this will be a bare repository (ending in
      `.git`).

 +  **`BUILD_DIRECTORY`**:
    In order to access the files when you push, the repository will be
      checked out here.
    **This directory will be deleted on every push.**

 +  **`PUBLIC_DIRECTORY`**:
    The directory that your server serves files from.
    **This directory will be deleted on every push,** so if you need to
      serve additional files (i·e those not generated by this script),
      you should place those files in a different directory and adjust
      your server configuration accordingly.
    Note that the `post-receive` script and associted H·T·M·L files
      provided by this repository expect a certain server configuration
      described below.

  +  **`PUBLIC_URL`**:
     The U·R·L that you are serving your site from (with no trailing
       slash).
     You cannot serve Status.git from a subdirectory.

A `Makefile` is provided to make installing the `post-receive` script
  on your server easy (assuming you have `ssh` access).
You’ll need to supply some variables there, too :—

 +  **`USERNAME`**:
    Your SSH username.

 +  **`DOMAIN`**:
    The domain or other address of your site.

 +  **`GIT_REPOSITORY`**:
    The absolute path to this Git repository, as above.

## Server Configuration

Your server should be configured to serve the following files from the
  provided `PUBLIC_DIRECTORY` in response to the following requests :—

### H·T·M·L responses

These responses **must** be served with a `Content-Type` of
  `text/html;charset=UTF-8` (or equivalent).
Note that these paths **do not** have a trailing slash.

 +  **`GET /`**:
    Serve the file at `/index.html`.

 +  **`GET /statuses`**:
    Serve the file at `/.statuses.html`.
    A `Link` header with the value
      `</statuses.jsonld>;rel=meta;type="application/ld+json"` (or
      equivalent) **must** be provided.

 +  **`GET /$YYYY-MM`** (where `$YYYY-MM` is an `xsd:gYearMonth`):
    Serve the file at `/.topic.html`.
    A `Link` header with the value
      `</$YYYY-MM.jsonld>;rel=meta;type="application/ld+json"` (or
      equivalent) **must** be provided.

 +  **`GET /$YYYY-MM/*`** (where `$YYYY-MM` is an `xsd:gYearMonth`):
    Serve the file at `/.status.html`.
    A `Link` header with the value
      `</$YYYY-MM.jsonld>;rel=meta;type="application/ld+json"` (or
      equivalent) **must** be provided.

 +  **`GET /topics`**:
    Serve the file at `/.topics.html`.
    A `Link` header with the value
      `</topics.jsonld>;rel=meta;type="application/ld+json"` (or
      equivalent) **must** be provided.

 +  **`GET /topics/$TOPIC`** (where `$TOPIC` matches `[0-9A-Za-z_-]+`):
    Serve the file at `/.topic.html`.
    A `Link` header with the value
      `</$TOPIC.jsonld>;rel=meta;type="application/ld+json"` (or
      equivalent) **must** be provided.

 +  **`GET /topics/$TOPIC/*`** (where `$TOPIC` matches
      `[0-9A-Za-z_-]+`):
    Serve the file at `/.status.html`.
    A `Link` header with the value
      `</$TOPIC.jsonld>;rel=meta;type="application/ld+json"` (or
      equivalent) **must** be provided.

### Json‐L·D responses

These responses **should** be served with a `Content-Type` of
  `application/ld+json`.
In all cases, for `/$PATH.jsonld`, this just serves the file at
  `/$PATH/index.jsonld`.

 +  **`GET /statuses.jsonld`**:
    Serve the file at `/statuses/index.jsonld`.

 +  **`GET /$YYYY-MM.jsonld`** (where `$YYYY-MM` is an
      `xsd:gYearMonth`):
    Serve the file at `/$YYYY-MM/index.jsonld`.

 +  **`GET /topics.jsonld`**:
    Serve the file at `/topics/index.jsonld`.

 +  **`GET /topics/$TOPIC.jsonld`** (where `$TOPIC` matches
      `[0-9A-Za-z_-]+`):
    Serve the file at `/topics/$TOPIC/index.jsonld`.

### Other Headers

All responses **should** have a `Access-Control-Allow-Origin` header
  with a value of `*` (assuming your server does not use credentials
  and is not being served behind a firewall).

## Committing Statuses To Git

The `post-receive` script will run whenever you make a commit to the
  `live` branch, which should be set as your default.
Statuses are represented by a small collection of files committed to
  particular locations in this repository :—

 +  Files committed to `/YYYY/MM/DD/HH.MM.SSZ/` (the final component
      can actually take any form but a time is **recommended**) are
      ordinary statuses.

 +  Files committed to `/topic/TOPICNAME/a/b/c/d/xxxx` (where `a`, `b`,
      `c`, and `d` are lowercase hexadecimal digits and `xxxx` can be
      anything) is a status posted to a specific topic (`TOPICNAME`).
    It is **recommended** that you make `a/b/c/d` the first four digits
      of an MD5 hash of the status content and `xxxx` the remaining
      digits (security is not an issue here, so MD5 is fine).
    `TOPICNAME` should have the form `[0-9A-Za-z_-]+`.

The intent is that “ordinary statuses” are a bit more ephemeral whereas
  “topic statuses” can serve as reference material.
Using an MD5 hash for topic statuses ensures you don’t post the same
  thing twice.

The files which represent a status are as follows :—

 +  **`text`**:
    The text of the status.
    Blank lines separate paragraphs; linebreaks will be preserved.
    There is a special markup for links: `<https://link.example>`, or
      `<https://link.example>="link text"` if you want to supply link
      text.
    At present, no other markup is supported.

 +  **`0=x_status_git_1.0`**:
    This file is **optional** and not currently used for anything, but
      indicates that the post follows the `1.0` format.
    The contents of this file, if present, **must** be
      `x_status_git_1.0`, optionally followed by a trailing newline.

 +  **`1=NAME`** (where `NAME` might be anything):
    This file is **recommended** and indicates the author of the
      status.
    Only one author is currently supported.
    The value of `NAME` **must** give the name of the author of the
      status.
    The contents of this file **must** give the author’s URL,
      optionally followed by a trailing newline.

 +  **`3=YYYY-MM-DD`** (where `YYYY-MM-DD` is a date):
    This file is **required** and indicates the date of the status.
    Only one date is currently supported.
    The value of `YYYY-MM-DD` **should** give the date of the status,
      although this is not used (in favour of the full timestamp).
    The contents of this file **must** give the full `xsd:dateTime`
      timestamp of the status, optionally followed by a trailing
      newline.

 +  **`4=IDENTIFIER`** (where `IDENTIFIER` might be anything):
    This file is **required** and provides an identifier for the
      status.
    Only one identifier is currently supported.
    The contents of this file **must** be an I·R·I which uniquely
      identifies the status (for example, a U·U·I·D or `tag:` URI).
    The value of `IDENTIFIER` **must** be a locally‐unique identifier
      for the status and **should** resemble the contents where
      possible.
    (Note, however, that `IDENTIFIER` cannot contain slashes and need
      not be a valid I·R·I.)

Files with names that begin with the strings `2=` or `x_status_git_`
  are reserved for backwards‐compatible extensions.

Status.git has no opinion on how these files make their way into the
  Git repository, except that all the files for a single status should
  be added in the same commit.
The intention is that the simple nature of these files will make them
  easy to automate.

## I Am Computer, How Do I Get Status?

Assume you are given a U·R·L `resource_url` which you think points to
  some kind of Status.git resource.
Start by resolving it as follows :—

01. Make a `HEAD` request to `resource_url`.

02. If there is a `Link` header with a `rel` of `meta` and a `type` of
      `application/ld+json`, set `resource_url` to the URL provided in
      that header and restart these steps from step 1.

03. Make a `GET` request to `resource_url` and let `response` be the
      response.

04. Set `document` as follows :—

    01. If the `Content-Type` header of `response` has a type of `text`
          and subtype of `html`, let `document` be the result of
          processing the body of `response` into a D·O·M tree as an
          H·T·M·L document .
        It is an error if this process fails.

    02. If the `Content-Type` header of `response` has a type of
          `application` and a subtype which is `xml` or which ends in
          `+xml`, let `document` be the result of processing the body
          of `response` into a D·O·M tree as an X·M·L document .
        It is an error if this process fails.

    03. Otherwise, let `document` be null.

05. If `document` is not null :—

    01. If there is a `<link>` element in either the H·T·M·L namespace
          or the Atom namespace in `document` with a `rel` of `meta`
          and a `type` of `application/ld+json`, set `resource_url` to
          the `href` of that `<link>` element and restart these steps
          from step 1.
        If multiple such elements exist, choose the first one.

    02. Otherwise, it is an error.

06. If the body of `response` is not a Json document, it is an error.

Assuming the U·R·L you were given was valid, you will end this
  algorithm with a Json‐L·D response, and you can use the `@type`
  attribute to determine the response type.
`@type` will be either a string or an array.

 +  If the `@type` is or contains `Forum`, the resource is a collection
      of topics.

 +  If the `@type` is or contains `Thread`, the resource is a
      collection of statuses.

The items in the collection may be determined through one of the
  following methods :—

 +  If the `@type` is or contains `OrderedCollectionPage`, then its
      `items` will be an array of resources.
    This is a partial collection, and the `prev` and `next` properties
      can be used to access further items from the parent collection
      (indicated by `partOf`).

 +  If the `@type` is or contains `OrderedCollection`, and the resource
      has `first` and `current` properties, then the `items` property
      will not be present.
    `first` and `current` can be accessed to provide
      `OrderedCollectionPage`s listing the items of the collection as
      above.

 +  Otherwise, the `items` property will contain every item in the
      collection.

Statuses themselves have the following properties :—

 +  **`@id`**:
    The identifier of the status.

 +  **`@type`**:
    The value `MicroblogPost`.

 +  **`created`** [`dcterms:created`]:
    The creation date for the status, as an `xsd:dateTime`.

 +  **`creator`** [`dcterms:creator`] (optional):
    The author of the status, as an object with an `@id` and `name`
      [`foaf:name`].

 +  **`identifier`** [`dcterms:identifier`]:
    An I·R·I which uniquely identifies the status.
    This differs from the `@id` in that it is not expected to be
      dereferenceable.

 +  **`subject`** [`dcterms:subject`] (optional):
    The topic of the status, for topic statuses.

 +  **`content`** [`sioc:content`]:
    The content of the status, as an `rdf:XMLLiteral`.
