💾 Archived View for alpha.lyk.so › small-internet › spartan captured on 2023-12-28 at 15:15:53. Gemini links have been rewritten to link to archived content
⬅️ Previous capture (2022-03-01)
-=-=-=-=-=-=-
Spartan protocol is simpler than
and does not use TLS. This makes it significantly less computationally expensive and thus friendlier to very old machines. It also simplifies the whole codebase for clients and servers. TLS is difficult to get right and is seen by some as a source of "hidden complexity" in Gemini. Spartan thus sits between Gemini and Gopher in the same way that Gemini sits between Gopher and the Web.
Be aware that the lack of TLS also means there is no encryption protecting anything sent over the Spartan network. One should *not* send, e.g., credit card numbers over this protocol.
This document is meant to be complementary to
the official Spartan specification
and contains much of the same information, simply stated differently. The "hands-on learning" section is, at the time of writing, novel, but the rest is meant to aid understanding of the official document by doing little more than rephrasing it. If this document and the official specification differ in any way, defer to the official specification.
Spartan servers listen for TCP connections on port 300 by default. They accept requests in this form:
<hostname> <path> <data length> [data]
A line break, as contained in the example above, is defined as a carriage return followed by a line feed. This two-character string is referred to in the Spartan specification (and in this document from this point on) as CRLF, and may, in many programming languages, be represented by the string "\r\n".
At length: a request to a Spartan server is defined as the host name, followed by a single space, followed by the path requested, followed by another space, followed by the length in bytes of the "data block" being submitted to the server (zero if the client is submitting no data block), followed by a CRLF, then optionally followed by the data the client wishes to submit to the server.
The server will then send a response in this form and close the connection once it is done sending data:
<status code> <status data> [response body]
At length: the server will respond with a single-digit status code, followed by a single space, followed by whatever data may be relevant to the response code, followed by a CRLF, and then, if the status code is "2", followed by the data requested by the client.
There are only four status codes, numbered two through five.
ASCII is the only encoding supported for request and response headers. Unicode may not be included in request or response headers.
While any data whatsoever may be served over Spartan, the default document format is an extension of
There is one additional line type in Spartan's version of gemtext markup, the "prompt line." This line type removes the need for Gemini's "10 INPUT" response code and is meant to function similarly to a single-input "form" element in HTML:
=:<whitespace><url>[<whitespace><human-friendly text>]
At length: a "prompt line" is defined as the string "=:", followed by an arbitrary number of whitespace characters, followed by a URL, optionally followed by more whitespace and then some human-readable text indicating the purpose of the input field.
Spartan URLs begin with "spartan://" and have the same structure as the HTTP URLs you may already be familiar with from the Web. The full structure may be defined as:
<scheme>://[<user>@]<host>[:<port>]/<path>[;<parameters>][?<query>][#<fragment>]
The "fragment", "user", and "parameters" components are allowed, but they aren't used. The "query" component should be used by clients to populate the "data block" portion of the request. The "host", "port", and "path" components should be used by the client for the "host", "port", and "path" portions of the Spartan request, as one might expect.
The Spartan specification has some good examples which will not be reproduced here.
Spartan is simple enough that writing requests by hand is not an entirely unreasonable thing to do, and doing so may help with improving your understanding of the protocol. If something is unclear, experimenting with a Spartan server somewhere can help clear things up.
For these examples, I'll be using netcat. Netcat is a common utility which allows sending raw, arbitrary data to a server over a TCP connection. Installing it on your machine should provide you with the "nc" command used in the following examples (which are meant to be run in your terminal).
If you're running Debian or one of its derivatives (e.g., Ubuntu, Linux Mint, Pop_OS!, etc.) you may be able to install netcat (if it's not already installed) with this command:
sudo apt install netcat-openbsd
Netcat can take input on stdin (piped or typed) and forward it to the server and port given as arguments.
As our first experiment, we'll make a request equivalent to loading the URL "spartan://mozz.us":
printf "mozz.us / 0\r\n" | nc mozz.us 300
We're using printf here because it does not by default print anything except what is in the argument given it.
The server ought to respond to this request with a header line starting with status code 2, followed by the body of mozz.us's Spartan site (a gemtext document at the time this was written).
Requesting a non-existent document should return a header line with status code 4:
printf "mozz.us /non-extistent 0\r\n" | nc mozz.us 300
And requesting a directory without a trailing slash from the server running at mozz.us should, at the time of writing, return a redirect:
printf "mozz.us /ufo 0\r\n" | nc mozz.us 300
Finally, we can sign the mozz.us guestbook by sending some data to spartan://mozz.us/guestbook/sign:
printf "mozz.us /guestbook/sign 18\r\nHello from netcat!" | nc mozz.us 300
(The string "Hello from netcat!" is 18 bytes long, which is where the "18" comes from in the command above.)
You can confirm that you have successfully signed the guestbook by requesting the guestbook page and looking at the latest entry (which should be at the bottom of the page):
printf "mozz.us /guestbook/ 0\r\n" | nc mozz.us 300
We've now demonstrated all the types of requests and responses possible under the Spartan protocol. What follows is the "Backus-Naur form" (BNF) grammar for the protocol's syntax, a formal specification which may act as a compact reference for those building Spartan clients and servers. It should simply state, using far fewer characters, what has already been stated more verbosely in this document.
As copied from the specification:
request = request-line [data-block] request-line = host SP path-absolute SP content-length CRLF reply = success / redirect / client-error / server-error success = '2' SP mimetype CRLF body redirect = '3' SP path-absolute CRLF client-error = '4' SP errormsg CRLF server-error = '5' SP errormsg CRLF content-length = 1*DIGIT data-block = *OCTET mimetype = type '/' subtype *(';' parameter) body = *OCTET errormsg = 1*(WSP / VCHAR) ; host from RFC 3986 ; path-absolute from RFC 3986, excluding empty string "" ; type from RFC 2045 ; subtype from RFC 2045 ; parameter from RFC 2045 ; CRLF from RFC 5234 ; DIGIT from RFC 5234 ; OCTET from RFC 5234 ; SP from RFC 5234 ; WSP from RFC 5234 ; VCHAR from RFC 5234