# Publishing wiki documents

A wiki is documents written where your code is and published here by whatever builds them. There is
no editor on this site: a page somebody has to remember to update drifts from the thing it
describes, and these are read by people writing against a contract.

Any module can have one. It is read at `/{module}/Wiki` by anybody - documentation nobody can read
is not documentation - and written at `/{module}/api/wiki` with a key granted that module's `wiki`
backend. Keys are given out from the site's permissions page.

`{module}` is the module's name in an address, as in `Mccp`. Today MCCP is the only one with a
wiki; the shape below is the same for any other.

> **MCCP's older address.** MCCP's build agent has published to `/api/mccp/wiki/{path}` since
> before the wiki belonged to the site, and that address still answers exactly as it did. New
> publishers should use `/Mccp/api/wiki/{path}`.

---

Documents, at their own addresses under `/{module}/Wiki`, uploaded by whatever builds them. There is no
editor on the site: they are written where the code is, so they cannot drift from it the way a page
somebody has to remember to update does.

**A document is the whole page.** It is served with nothing of this site around it - no header, no
footer, no stylesheet of ours - so send a complete HTML document and lay it out however you like.
The wiki has the browser window to itself.

**`/{module}/Wiki` serves the index.** The rule is short:

- **One document**: it is the wiki, whatever it is called. There is no choice to make, so nothing
  has to be named `index`.
- **More than one**: the one called `index` is what `/{module}/Wiki` serves. Which document a reader
  should start at is yours to decide, and it is the one thing this site will not decide for you -
  so several documents with none of them `index` means `/{module}/Wiki` answers `404`, and the publish
  that caused it says so in a `warning` field.

Uploading with **no path** is the same thing as uploading to `index`; the front document has one
name, not two spellings. Nothing here is generated - a list of pages written by this site is this
site writing your wiki, which is what the first version of this did and why it stopped.

**Addresses carry no extension.** These are raw `.html` files at your end, and the address is the
name without it: `/{module}/Wiki/packaging`, never `/{module}/Wiki/packaging.html`. A path with a dot in it
is a `400`.

```
POST /{module}/api/wiki/{path}?title=Packaging%20a%20mod
X-Api-Key: <a key granted {module}:wiki>
Content-Type: text/html

<h1>Packaging a mod</h1>
<p>...</p>
```

`201` the first time a path is used, `200` after that, with
`{ path, title, url, bytes, sha256, createdAt, updatedAt, history, unchanged, warning }`.

**Sending the same bytes again does nothing at all.** If what you send is byte for byte what is
already stored, the answer is `200` with `"unchanged": true`, no version is kept, and `updatedAt`
still says the day the document last actually changed. A build that regenerates twenty files
nightly can send all twenty; the nineteen that did not change leave no trace.

`warning` is null in the normal case. It is a sentence when the upload has left the wiki with
several documents and no `index`, which means `/{module}/Wiki` is answering `404` - said at the moment
it becomes true, to the only party who can fix it.

**The body is the page and it is served exactly as it arrived**, byte for byte. Nothing is filtered
out of it. That is deliberate: the gate is the key, and anybody holding one can already publish the
desktop app, which runs on people's machines.

**An upload replaces one document, and only that one.** Two halves worth saying separately:

- *That* document is replaced **whole** - nothing of the previous version survives, because what
  you sent is a file that lives somewhere else and half an old page inside a new one is a state
  that file cannot describe.
- Every **other** document is left exactly as it was. Publish one file and only that file moves;
  there is no need to re-send the wiki to change a page of it, and no way to lose a document by
  forgetting to include it.

So a build that touches one page uploads one page. A build that regenerates everything uploads each
file it made, in any order, and anything it did not send is still there afterwards.

Removing a document is the one thing an upload cannot do - that is `DELETE`, below, and it is
deliberate: a file dropped from your repository should not vanish from the site because a publish
happened to omit it.

### What a page used to say

Replacing a document keeps the version it replaced. Nothing is thrown away, and every document
carries a **Version history** link at the bottom of it:

```
GET /{module}/Wiki/versions/{path}          the history of that document
GET /{module}/Wiki/versions/{path}?at={id}  that version, as it was
```

The history lists the current version and every earlier one, newest first, with the date each was
published and the date it was replaced. Opening an older one serves exactly what was uploaded then,
with a line at the bottom saying which version it is and offering the current one - somebody who
arrives from a search result has to be able to tell.

`history` in the publish answer is the address of that page, so a client never has to build it.

**`versions` is reserved.** Nothing can be published at `/{module}/Wiki/versions` or under it -
`{"error":"reserved_path"}` - because everything there is a history rather than a document.

**The one thing this site adds to a page.** That footer line is appended before the closing
`</body>`, so it lands inside the layout your document set up rather than dangling after it. It is
the only exception to serving a document byte for byte, and it exists because a history nobody can
find from the page is a history nobody reads.

Deleting a document deletes its history with it. Somebody taking down a page they should not have
published needs it gone, not readable one link away.

| | |
| --- | --- |
| `path` | Lowercase letters, digits and dashes, in segments: `packaging`, `api/uploading-a-mod`. No dots, so no extensions. Empty means `index`. Capitals are folded, so one document cannot exist twice under two spellings. Anything else is a `400` |
| `title` | Optional. Without it the document is named from its own `<h1>`, or its `<title>`, or failing both its path |
| Size | 512 KB, and a larger document is a `413` |

```
DELETE /{module}/api/wiki/{path}
X-Api-Key: <a key granted {module}:wiki>
```

`204`, or `404` if there is nothing there. For a page that was renamed at the other end - the only
thing that knows it is stale is the program that used to write it.

**Reading takes nothing.** Every page is public at its own address, and `GET /{module}/api/wiki` lists
what exists - that one is for the program writing the pages, which needs to know what it wrote last
time. Documentation somebody has to sign in for is documentation they cannot read before
deciding whether to write against this at all.

### Reading one back

```
GET /{module}/api/wiki/{path}
```

```json
{ "path": "packaging", "title": "Packaging a mod", "url": "/{module}/Wiki/packaging",
  "bytes": 4821, "sha256": "9f2c...", "createdAt": "...", "updatedAt": "..." }
```

`404` if nothing is there. No key: this says nothing the page itself does not.

**Check a publish landed with the hash, not the timestamp.** A title and an `updatedAt` both move
when a page is written and neither says what was written - two pages on this site served somebody
else's test stub for hours with their own titles and dates intact beside them. Hash your source,
compare, and you know.

The bytes themselves are at the page's own address, `/{module}/Wiki/{path}`, which needs nothing
either - so a client that wants to diff rather than compare can fetch them.

---
