twins Gemini server

twins configuration


twins requires a configuration file to operate. It is loaded from
~/.config/twins/config.yaml by default. You may specify a different location
via the "--config" argument.

Listen


Address to listen for connections on in the format of interface:port.

Listen on all addresses (IPv4 and IPv6)


:1965

Listen on a specific address (IPv4 or IPv6)


192.0.2.1:1965 or "[2001:db8::1]:1965" (please note the quotes and brackets
for IPv6 addresses).

Listen on localhost (IPv4 only)


localhost:1965

Types


Content types may be defined by file extension. When a type is not defined for
the requested file extension, content type is detected automatically.


ShowImages


When enabled, clients accessing gemini pages via HTTPS will see links to images
as inline images.

Hosts


Hosts are defined by their hostname followed by one or more paths to serve.


Paths may be defined as fixed strings or regular expressions (starting with "^").

Any path not matching a specific page, file name or file extension should end
in a trailing slash, signifying that it is a directory. Visitors are
automatically redirected when accessing a directory path without including a
trailing slash.

Paths are matched in the order they are defined.

When accessing a directory the file index.gemini or index.gmi is served.

When a host is defined with the name "default", other hosts and paths will use
those values as the default configuration. It is not currently possible to
enable an attribute by default and then disable it for individual paths.

Certificates


A certificate and private key must be specified.

Self-signed domain certificate (recommended)


Use openssl to generate a certificate for a domain.

Replace rocketnine.space and twins.rocketnine.space with your domain and subdomain.

openssl req -x509 -out localhost.crt -keyout localhost.key \
  -newkey rsa:2048 -nodes -sha256 \
  -days 36500 \
  -subj '/CN=localhost' -extensions EXT -config <( \
   printf "[dn]\nCN=rocketnine.space\n[req]\ndistinguished_name = dn\n[EXT]\nsubjectAltName=DNS:rocketnine.space,DNS:twins.rocketnine.space\nkeyUsage=digitalSignature\nextendedKeyUsage=serverAuth")

Self-signed localhost certificate


Use openssl to generate a certificate for localhost.

Some Gemini clients will not accept such a certificate when accessing a server via domain or subdomain.

openssl req -x509 -out localhost.crt -keyout localhost.key \
  -newkey rsa:2048 -nodes -sha256 \
  -days 36500 \
  -subj '/CN=localhost' -extensions EXT -config <( \
   printf "[dn]\nCN=localhost\n[req]\ndistinguished_name = dn\n[EXT]\nsubjectAltName=DNS:localhost\nkeyUsage=digitalSignature\nextendedKeyUsage=serverAuth")

Signed certificate from Let's Encrypt


Use certbot to get a certificate from Let's Encrypt for a domain.

certbot

Let's Encrypt

certbot certonly --config-dir /home/www/certs \
  --work-dir /home/www/certs \  
  --logs-dir /home/www/certs \
  --webroot \
  -w /home/www/gemini.rocks/public_html \
  -d gemini.rocks \
  -d www.gemini.rocks

Provide the path to the certificate file at certs/live/$DOMAIN/fullchain.pem
and the private key file at certs/live/$DOMAIN/privkey.pem to twins.

StyleSheet


Provide the path to a style sheet to serve instead of the default style sheet.

This option only applies when serving HTTPS connections.

DisableHTTPS


Pages are also available via HTTPS on the same port by default.
Set this option to "true" to disable this feature.

Pages are converted automatically by gmitohtml.

gmitohtml

DisableSize


The size of the response body is included in the media type header by default.
Set this option to "true" to disable this feature. See the proposals document
for more information.

Proposals

Path


Resources


One resource must be defined for each path.

Root

Serve static files from specified root directory.

Command

Serve output of system command.

When input is requested from the user, it is available as a pseudo-variable
$USERINPUT which does not require surrounding quotes. It may be used as an
argument to the command, otherwise user input is passed via standard input.

Proxy

Forward requests to Gemini server at specified URL.

Use the pseudo-scheme gemini-insecure:// to disable certificate verification.

Redirect

Redirect requests to specified path or URL.

Attributes


Any number of attributes may be defined for a path.

Cache

Cache duration (in seconds). Set to "0" to disable caching entirely. This is an
out-of-spec feature. See the proposals document for for more information.

Proposals

FastCGI

Forward requests to FastCGI server at specified address or path.

A "Root" attribute must also be specified.

Connect via Unix socket:

unix:///var/run/php.sock

Connect via TCP:

tcp://127.0.0.1:9000

FastCGI on Wikipedia

Hidden

When enabled, hidden files and directories may be accessed. This attribute is
disabled by default.

Input

Request text input from user.

Lang

Specifies content language. This is sent to clients via the MIME type "lang" parameter.

List

When enabled, directories without an index file will serve a list of their
contents. This attribute is disabled by default.

Log

Path to log file. Requests are logged in Apache format, excluding IP address and query.

Apache log format

SensitiveInput

Request sensitive text input from the user. Text will not be shown as it is entered.

SymLinks

When enabled, symbolic links may be accessed. This attribute is disabled by default.

Type

Content type is normally detected automatically. This attribute forces a
specific content type for a path.

End-of-line indicator


The Gemini protocol requires "\r\n" (CRLF) as the end-of-line indicator. This
convention is carried over from protocol specifications **first written in the
1970s**. This requirement is antithetic to the spirit of Gemini (to improve
upon the Finger and Gopher protocols), increasing the complexity of client and
server implementations unnecessarily.

In anticipation of an improvement to the Gemini specification, administrators
may configure twins to send standard "\n" (LF) line endings by setting
"SaneEOL" to "true".

References:
1
2
3
4

Example config.yaml


# Address to listen on
listen: :1965

# Custom content types
types:
  .json: application/json; charset=UTF-8
  .js: application/javascript; charset=UTF-8

# Hosts and paths to serve
hosts:
  default: # Default host configuration
    paths: # Default path attributes
      -
        lang: en
        log: /srv/log/gemini.log
        symlinks: true # Follow symbolic links
  gemini.rocks:
    cert: /srv/gemini.rocks/data/cert.crt
    key: /srv/gemini.rocks/data/cert.key
    stylesheet: /srv/gemini.rocks/style.css
    paths:
      -
        path: ^/.*\.php$
        root: /home/geminirocks/public_html
        fastcgi: unix:///var/run/php.sock
      -
        path: /files/
        root: /home/geminirocks/files
        cache: 604800 # Cache for 1 week
        list: true # Enable directory listing
      -
        path: ^/(help|info)/$
        root: /home/geminirocks/docs/help
      -
        path: /proxy-example/
        proxy: gemini://localhost:1966
      -
        path: /cmd-example
        command: uname -a
        cache: 0 # Do not cache
      -
        path: /
        root: /home/geminirocks/public_html
  twins.rocketnine.space:
    cert: /srv/twins.rocketnine.space/data/cert.crt
    key: /srv/twins.rocketnine.space/data/cert.key
    paths:
      -
        path: /redir-path-example
        redirect: /other-resource
      -
        path: /redir-url-example
        redirect: gemini://gemini.circumlunar.space/
      -
        path: /
        root: /home/twins/public_html