A simple server for the Gemini protocol, with an API inspired by Sinatra.


Use the built-in executable to serve the current directory.

$ gem install gemini_server
Successfully installed gemini_server-0.1.0
1 gem installed
$ gemini_server -h
Usage: gemini_server [options]
    -p, --port PORT                  Port to listen on
        --cert-path PATH             Path to cert file
        --key-path PATH              Path to key file
        --charset CHARSET            Charset of text/* files
        --lang LANG                  Language of text/* files

Or require the library to declare custom routes in Ruby.

require "gemini_server"

server =

server.route("/hithere/:friend") do
  if params["friend"] == "Gary"
    gone "Gary and I aren't on speaking terms, sorry."
    success "Hi there, #{params["friend"]}!"

server.route("/byebye") do
  lang "pig-latin"
  success "arewellfay!"

server.listen("", 1965)

Initialization options

A SSL certificate. Either a OpenSSL::X509::Certificate object, or a string.
Path to a SSL certificate file. Defaults to the value of the env variable GEMINI_CERT_PATH. Ignored if cert option is supplied.
A SSL key. Either a OpenSSL::PKey object, or a string.
Path to a private key file. Defaults to the value of the env variable GEMINI_KEY_PATH. Ignored if key option is supplied.
Sets the default MIME type for successful responses. Defaults to text/gemini, or inferred by the name of the file being served.
If set, includes the charset in the response's MIME type.
If set, includes the language in the response's MIME type, if the MIME type is text/gemini. Per the Gemini spec, "Valid values for the "lang" parameter are comma-separated lists of one or more language tags as defined in RFC4646."
Path to a location from which the server will serve static files. If not set, the server will not serve any static files.
Path to the location of ERB templates. If not set, defaults to current directory.

* The option pairs cert and cert_path, and likewise key and key_path, are mutually exclusive, so they are technically optional. But per the Gemini spec, connections must use TLS, so it is a runtime error if neither option, nor either of the fallback env variables, are used.

Route handlers

To define a route handler, use GeminiServer#route:

server =
server.route("/path/to/route/:variable") do
  # route logic

The route method takes a Mustermann matcher string and a block.

Within the block, code has access to these methods:

Returns a hash of params parsed from the request path.
Returns the full URI of the request.
Sets the MIME type of the response.
Sets the charset of the response, overriding the server's default charset.
Sets the lang of the response, overriding the server's default lang.
erb(filename, locals: {})
Renders an ERB template located at filename, then sets status to success. MIME type is inferred by the template extension. The template will have access to any instance variables defined in the handler block, as well as any local variables passed in via the locals keyword param.
respond(code, meta, body=nil)
Sets the response code, meta, and optional body. It's probably easier to use erb method, or any of the convenience status methods in the next section.

ERB templates

Using an ERB template automatically sets the status to 20 (success) because a success is the only type of response that can contain a body. It also tries to infer the MIME type from the template extension (excluding any .erb).

ERB rendering can define local variables, like in Sinatra:

server.route("/hithere/:friend") do
  erb "hithere.gmi", locals: { friend: params["friend"] }
<!-- hithere.gmi.erb -->
# Hi there!

Hi there, <%= friend %>.

ERB templates have the params hash available as a local var:

server.route("/hithere/:friend") do
  erb "hithere.gmi"
<!-- hithere.gmi.erb -->
# Hi there!

Hi there, <%= params["friend"] %>.

Status methods

Each of these methods are available within a route handler block. Forgetting to use a status method defaults to a temporary failure. See Gemini Specification for an explanation of each response status.

  • input(prompt)
  • sensitive_input(prompt)
  • success(body, mime_type=nil)
  • redirect_temporary(url)
  • redirect_permanent(url)
  • temporary_failure(explanation = "Temporary failure")
  • server_unavailable(explanation = "Server unavailable")
  • cgi_error(explanation = "CGI error")
  • proxy_error(explanation = "Proxy error")
  • slow_down(delay)
  • permanent_failure(explanation = "Permanent failure")
  • not_found(explanation = "Not found")
  • gone(explanation = "Gone")
  • proxy_request_refused(explanation = "Proxy request refused")
  • bad_request(explanation = "Bad request")
  • client_certificate_required(explanation = "Client certificate )
  • certificate_not_authorized(explanation = "Certificate not )
  • certificate_not_valid(explanation = "Certificate not valid")

Static file serving

To serve static files, set the initialization option public_folder to the location of your static files. If no route handlers match a request, the server will look for a static file to serve in that location instead. If the public_folder option is unset, no static files will be served.