💾 Archived View for gmb.is › gemengine captured on 2020-09-24 at 00:40:48. Gemini links have been rewritten to link to archived content

View Raw

More Information

-=-=-=-=-=-=-

Gemengine

Gemengine is a small application server for Gemini, that should feel similar to micro web frameworks like Express, Sinatra, etc.

Examples

The obligatory "Hello, World" is as straightforward as you'd hope.

#lang typed/racket

(require gemengine)

(: index (-> request response))
(define (index _)
  (send-text "Hello, world!"))

(route "/" index)

(serve-forever 1965)

It's also easy to build a file server with Gemengine.

#lang typed/racket

(require gemengine)

(: file-serve (-> request response))
(define (file-serve req)
  (define filename (format "~a.gmi" (car [request-path req])))
  (send-file filename))

(route "/:path" file-serve)

(serve-forever 1965)

Features

Today, Gemengine is missing a lot of (even very basic) features you may want out of an application server, but it has just enough to get you started:

Beyond these basics, Gemengine does have a few nice security features. One of the goals of this project is to explore the way types can be used to create more user friendly and secure library APIs.

For example, consider a redirect endpoint, which you want to use to redirect to arbitrary pages on your gemsite. You may implement it using query strings so that "/redirect?endpoint" would redirect to "/endpoint". The code for that may look like this:

(: insecure-redirect (-> request response))
(define (insecure-redirect req)
  (define input (request-query-string req))
  (redirect input))

(route "/redirect" insecure-redirect)

Now, this technically has a minor vulnerability known as an "arbitrary redirect". A malicious person in the gemverse may post a link to your site, "gemini://example.com/redirect?gemini://evil-example.com". Users who trust your site, may click the link expecting to end up somewhere familiar, but will inadvertantly be redirected somewhere dangerous.

Gemengine makes a small attempt to correct such errors, and, in this case, the code will result in a type error.

Type Checker: type mismatch
  expected: SecureString
  given: InsecureString
  in: input

To fix this, we can sanitize the input. Perhaps the simplest way to solve this issue, is by prefixing the route with the protocol and host.

(: secure-redirect (-> request response))
(define (secure-redirect req)
  (define input (request-query-string req))
  (redirect (format "gemini://gmb.is/~a" input)))

(route "/redirect" secure-redirect)

By taking this approach, we can ensure that our redirect is always host local.

Getting started

If you are interested in trying Gemengine out, or if you want to follow along with the development, you can find the source code on Github:

https://github.com/Lab3301/gemengine

While development is still in an exploratory stage, you are likely to find documentation lacking, and the API unstable. Feel free to find me on IRC if you have any questions :)

Home