3 A minimal git‐based self‐hosted status publishing solution.
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 /$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 /$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 ### Json‐L·D responses
135 These responses **should** be served with a `Content-Type` of
136 `application/ld+json`.
137 In all cases, for `/$PATH.jsonld`, this just serves the file at
138 `/$PATH/index.jsonld`.
140 + **`GET /about.jsonld`**:
141 Serve the file at `/about/index.jsonld`.
143 + **`GET /statuses.jsonld`**:
144 Serve the file at `/statuses/index.jsonld`.
146 + **`GET /$YYYY-MM.jsonld`** (where `$YYYY-MM` is an
148 Serve the file at `/$YYYY-MM/index.jsonld`.
150 + **`GET /topics.jsonld`**:
151 Serve the file at `/topics/index.jsonld`.
153 + **`GET /topics/$TOPIC.jsonld`** (where `$TOPIC` matches
155 Serve the file at `/topics/$TOPIC/index.jsonld`.
159 All responses **should** have a `Access-Control-Allow-Origin` header
160 with a value of `*` (assuming your server does not use credentials
161 and is not being served behind a firewall).
163 ## Committing Statuses To Git
165 The `post-receive` script will run whenever you make a commit to the
166 `live` branch, which should be set as your default.
167 Statuses are represented by a small collection of files committed to
168 particular locations in this repository :—
170 + Files committed to `/YYYY/MM/DD/HH.MM.SSZ/` (the final component
171 can actually take any form but a time is **recommended**) are
174 + Files committed to `/topic/TOPICNAME/a/b/c/d/xxxx` (where `a`, `b`,
175 `c`, and `d` are lowercase hexadecimal digits and `xxxx` can be
176 anything) is a status posted to a specific topic (`TOPICNAME`).
177 It is **recommended** that you make `a/b/c/d` the first four digits
178 of an MD5 hash of the status content and `xxxx` the remaining
179 digits (security is not an issue here, so MD5 is fine).
180 `TOPICNAME` should have the form `[0-9A-Za-z_-]+`.
182 The intent is that “ordinary statuses” are a bit more ephemeral whereas
183 “topic statuses” can serve as reference material.
184 Using an MD5 hash for topic statuses ensures you don’t post the same
187 The files which represent a status are as follows :—
190 The text of the status.
191 Blank lines separate paragraphs; linebreaks will be preserved.
192 There is a special markup for links: `<https://link.example>`, or
193 `<https://link.example>="link text"` if you want to supply link
195 At present, no other markup is supported.
197 + **`0=x_status_git_1.0`**:
198 This file is **optional** and not currently used for anything, but
199 indicates that the post follows the `1.0` format.
200 The contents of this file, if present, **must** be
201 `x_status_git_1.0`, optionally followed by a trailing newline.
203 + **`1=NAME`** (where `NAME` might be anything):
204 This file is **recommended** and indicates the author of the
206 Only one author is currently supported.
207 The value of `NAME` **must** give the name of the author of the
209 The contents of this file **must** give the author’s URL,
210 optionally followed by a trailing newline.
212 + **`2=TITLE`** (where `TITLE` might be anything):
213 This file is **optional** and indicates the title of the status.
214 The value of `TITLE` **should** be a file·system‐friendly version
215 of the title, but is ignored.
216 The contents of this file **must** give the title of the status,
217 optionally followed by a trailing newline.
219 + **`3=YYYY-MM-DD`** (where `YYYY-MM-DD` is a date):
220 This file is **required** and indicates the date of the status.
221 Only one date is currently supported.
222 The value of `YYYY-MM-DD` **should** give the date of the status,
223 although this is not used (in favour of the full timestamp).
224 The contents of this file **must** give the full `xsd:dateTime`
225 timestamp of the status, optionally followed by a trailing
228 + **`4=IDENTIFIER`** (where `IDENTIFIER` might be anything):
229 This file is **required** and provides an identifier for the
231 Only one identifier is currently supported.
232 The contents of this file **must** be an I·R·I which uniquely
233 identifies the status (for example, a U·U·I·D or `tag:` URI).
234 The value of `IDENTIFIER` **must** be a locally‐unique identifier
235 for the status and **should** resemble the contents where
237 (Note, however, that `IDENTIFIER` cannot contain slashes and need
238 not be a valid I·R·I.)
240 Files with names that begin with the strings `2=` or `x_status_git_`
241 are reserved for backwards‐compatible extensions.
243 Status.git has no opinion on how these files make their way into the
244 Git repository, except that all the files for a single status should
245 be added in the same commit.
246 The intention is that the simple nature of these files will make them
249 ## I Am Computer, How Do I Get Status?
251 Assume you are given a U·R·L `resource_url` which you think points to
252 some kind of Status.git resource.
253 Start by resolving it as follows :—
255 01. Make a `HEAD` request to `resource_url`.
257 02. If there is a `Link` header with a `rel` of `meta` and a `type` of
258 `application/ld+json`, set `resource_url` to the URL provided in
259 that header and restart these steps from step 1.
261 03. Make a `GET` request to `resource_url` and let `response` be the
264 04. Set `document` as follows :—
266 01. If the `Content-Type` header of `response` has a type of `text`
267 and subtype of `html`, let `document` be the result of
268 processing the body of `response` into a D·O·M tree as an
270 It is an error if this process fails.
272 02. If the `Content-Type` header of `response` has a type of
273 `application` and a subtype which is `xml` or which ends in
274 `+xml`, let `document` be the result of processing the body
275 of `response` into a D·O·M tree as an X·M·L document .
276 It is an error if this process fails.
278 03. Otherwise, let `document` be null.
280 05. If `document` is not null :—
282 01. If there is a `<link>` element in either the H·T·M·L namespace
283 or the Atom namespace in `document` with a `rel` of `meta`
284 and a `type` of `application/ld+json`, set `resource_url` to
285 the `href` of that `<link>` element and restart these steps
287 If multiple such elements exist, choose the first one.
289 02. Otherwise, it is an error.
291 06. If the body of `response` is not a Json document, it is an error.
293 Assuming the U·R·L you were given was valid, you will end this
294 algorithm with a Json‐L·D response, and you can use the `@type`
295 attribute to determine the response type.
296 `@type` will be either a string or an array.
298 + If the `@type` is or contains `Forum`, the resource is a collection
301 + If the `@type` is or contains `Thread`, the resource is a
302 collection of statuses.
304 + If the `@type` is or contains `Microblog`, the resource describes
305 this site as a whole.
306 The `streams` property will contain a list of available `Forum`s
307 and `Thread`s, as objects with an `@id` and `@type`.
309 The items in the collection (`Forum` or `Thread`) may be determined
310 through one of the following methods :—
312 + If the `@type` is or contains `CollectionPage` or
313 `OrderedCollectionPage`, then its `items` will contain resources.
314 This is a partial collection, and the `prev` and `next` properties
315 can be used to access further items from the parent collection
316 (indicated by `partOf`).
317 `first` and `current`, in this scenario, point “horizontally” to
318 the first and latest pages of items, not to subpages.
320 + If the `@type` is or contains `Collection` or `OrderedCollection`
321 and the resource has `first` and/or `current` properties, then
322 the `items` property will not be present.
323 `first` and `current` can be accessed to provide
324 `OrderedCollectionPage`s listing the items of the collection.
326 + Otherwise, the `items` property will contain every item in the
329 Statuses themselves have the following properties :—
332 The identifier of the status.
335 The value `MicroblogPost`.
337 + **`created`** [`dcterms:created`]:
338 The creation date for the status, as an `xsd:dateTime`.
340 + **`creator`** [`dcterms:creator`] (optional):
341 The author of the status, as an object with an `@id` and `name`
344 + **`identifier`** [`dcterms:identifier`]:
345 An I·R·I which uniquely identifies the status.
346 This differs from the `@id` in that it is not expected to be
349 + **`subject`** [`dcterms:subject`] (optional):
350 The topic of the status, for topic statuses.
352 + **`content`** [`sioc:content`]:
353 The content of the status, as an `rdf:XMLLiteral`.