The Titan Specification (mirror)

See the original here (it might be updated)

The Titan protocol is an add-on for Gemini clients and servers. It is used to upload data to servers, making external tools like ftp or other protocols like the web unnecessary.

Client and server do the usual TLS handshake and then the client sends the Titan URL:

“titan://transjovian.org/test/raw/testing;size=123;mime=plain/text;token=hello\r\n”

That is, the target URL, followed by one, two, or three parameters:

After the Titan URL and the carriage return and linefeed the client sends the data and the server replies with a regular Gemini status response.

The Titan URL

It is expected that a resources that you can read using Gemini may be writeable using Titan. A regular Gemini URL points to a "resource" and you use your Gemini client to read it. A Titan URL uses the "titan" scheme instead of "gemini". If you point your Titan-enabled client at this Titan URL, you can edit that resource.

A Titan URL uses extra parameters. Parameters are not query parameters! There is no question mark after the URL. Parameters are separated from the rest of the URL and each other using a semicolon and they come as key/value pairs. Here’s what the URL RFC says:

URI producing applications often use the reserved characters allowed in a segment to delimit scheme-specific or dereference-handler-specific subcomponents. For example, the semicolon (";") and equals ("=") reserved characters are often used to delimit parameters and parameter values applicable to that segment.

RFC 3986

Titan uses the following three parameters:

Gemini example URLs:

Titan example URL:

More:

The Titan Request

Why a token?

Use the token when anonymous users can make changes on the server. If only known users can make changes, no token is required: use the features Gemini already provides, client certificates.

Authentication & Authorisation

Why a MIME-type?

The server needs to know the MIME-type when it serves the page in the future. When the server returns a code 20 status response it must include a MIME type. When uploading text or files, the same principle applies: specify the MIME type you’re uploading using this parameter.

Sure, we could assume a list of well-known file name extensions and expect the URL to end in one, but that is very inflexible.

Not all file extensions are known: is it .jpg or .jpeg? .doc or .docx? .md or .markdown?

A file extension is not always used: do you allow a URL like “gemini://transjovian.org/test/testing”? If so, what is the MIME type you are assuming?

If you’re a developer, you can call “file --mime-type --brief” on a file. Or you can do a quick and dirty mapping yourself:

For text the user typed, use text/gemini.

If no MIME type is provided, the server should assume text/gemini.

Note that a server may reject your upload if it restricts the MIME types it accepts, and it may ignore or translate MIME types it gets. It may accept text/plain and treat it as text/gemini, for example.

Why a size?

When sending data over the Internet, it can arrive in chunks. If your connection is bad, chunks can be very small and there can be long pauses between them. Thus, a server is always wondering: is the upload finished? Did the client disconnect? Would you implement a timeout? If so, are you prepared to disallow long uploads from people with bad connections? This is hard to get right.

If we send the size before sending the data, the server knows exactly how many bytes it must read. That doesn’t solve all the problems, but it’s a start. You might still have to implement a timeout on the server side, for example, because otherwise malicious people could start thousands of uploads that never send any actual data. Each of these connections wastes resources on your server. That’s not good.

Typical interaction between client and server

Here’s how a simple file upload client (C) and server (S) interaction might look like:

C: Opens connection eg. transjovian.org:1965

S: Accepts connection

C/S: Complete TLS handshake

C: Validates server certificate

C: Sends upload intent URL (one CRLF terminated line) (see “File Intent” below)

S: If an error is detected, sends response header (one CRLF terminated line) and closes connection (see “Initial Response” below)

C: Sends data

S: Sends response header (one CRLF terminated line) (See “Confirmation” below)

C: Handles response

Validation

Once the server has the Titan URL, it might detect an error: the token is invalid, the size exceeds the internal limit, the filename doesn’t match the constraints, the MIME type is not allowed, a client certificate is required, and so on. In this case, the server responds with an error line and closes the socket, for example: “50 A token is required to upload a file\r\n”.

The server should reject the upload if it exceeds the intended size. Uploading a lot more in order to fill up disk space or memory are common denial of service attacks.

Client should check for a server response when the connection is closed.

Response

When the client has sent the file data, the server completes the transaction with a regular Gemini response before closing the connection. It could simply report success: “20 text/gemini; charset=UTF-8\r\nUpload succeeded.\n”, or it could redirect to the updated resource: “30 titan://transjovian.org/test/testing\r\n”, or it could report an error: “50 Error writing file: some exception\r\n”. Any Gemini response is possible.

Redirects

When the client edits a resource, it needs to view it first so that the user knows the original content. What to edit? A Gemini request is used to get this data using URL A. If this request results in a redirect to B, then that redirect is followed by another Gemini request. The Titan request must be made to that last Gemini URL in that redirect chain, i.e. B instead of A. Making a Titan request to a URL that would have resulted in a redirect using Gemini (URL A) is a mistake and ought to be reported as an error. If the server saves the file in such a way that it would be shown at the end of a chain of redirects (URL B), the the client is lucky. This is not mandated behaviour.

Creation

When the client visits a resource that could conceivably be created it can just use Titan to attempt it. Ideally, the previous Gemini request returned a 20 SUCCESS status, possibly with zero bytes of content to show, or a message like “Edit this text”. No such response is mandated, how ever. The server might also answer with a status 51 NOT FOUND.

Deletion

When the client wants to delete a resource, it uses the Titan protocol to send zero bytes of content (size=0).

If the server is a site that heads header or footer to pages, then people are tempted to edit the page including header and footer, and if the edit succeeds, the site then adds a second header and footer. Such sites need a link to a “raw” version of the page somewhere, containing just the page text to be edited.

If the server is a site that allows editing by multiple people, locking resources might be something to consider. In order for the server to know that a user is attempting to lock a page for editing, a “lock” URL has to be requested in order to lock a page.

One way to provide both is to define a new set of parameters for the Titan URL. Client and server do the usual TLS handshake and then the client sends the Titan URL:

“titan://transjovian.org/test/testing;edit\r\n”

That is, the target URL, followed by exactly one parameter: “edit”.

The response to this request is the content you need to edit the page. What you save is going to change the response to a Gemini request of the same URL without the “edit” parameter:

“gemini://transjovian.org/test/testing\r\n”

That is to say, the “raw” page without headers or footers or post-processing. It the content an author would need to make a change. Note that this doesn’t necessarily need to be text. The MIME type depends on the resource the user wants to edit. The original is not necessarily “text/gemini”.

The server is free to lock a page for editing or do anything else it wants to do knowing that the user intends to edit the page.

No mechanism is defined to remove the edit lock, so servers must necessarily use a timeout.

If a server uses edit locks and a user saves a page from the client and “goes back” the client might need to request the same URL again, using the Titan URL with the “edit” parameter, in order to create the edit lock. Whether this is mandatory or not is not defined.

If a server uses edit locks and the same IP requests the edit lock, it’s up to the server to decide whether it wants to treat this request as the same person trying to edit the page and that therefore this should succeed.

If a server uses edit locks and another user requests the Titan URL with the “edit” parameter for a page that is already locked, a 40 TEMPORARY FAILURE status with the <META> line explaining that the page is currently locked for editing.

The original Gemtext version of this page can be accessed with a Gemini client: gemini://blakes.dev/titan-spec.gmi

Gemini request details:

Original URL
gemini://blakes.dev/titan-spec.gmi
Status code
Success
Meta
text/gemini
Proxied by
A modified version of kineto

Be advised that no attempt was made to verify the remote SSL certificate.