## title: My Gopher application framework
       ## date: "2026-06-10"
       
       ## Introduction
       
       I've always really liked the Gopher protocol for its
       simplicity, lightness, and accessibility. A while back, I
       wanted to have my own solution for creating custom Gopher
       applications. That's why I decided to write a framework in
       Go that allows you to implement an RFC 1436 compliant Gopher
       server, as well as add evaluation extensions for the
       gophermap format and dynamic and virtual routes.
 (HTM) RFC 1436
       
       The name of the project is fleur, pronounced \\flœʁ\\, which
       means flower.
 (HTM) fleur
       
       ## My goal
       
       My goal is to provide an API for implementing Gopher
       applications. I want it to be easy to use while remaining
       low-level enough to allow for a high degree of flexibility.
       
       ## How it works
       
       There are a few core components to introduce in order to
       fully understand how the framework works.
       
       ### Route
       
       A route is a pair consisting of a regular expression and a
       function used by a router.
       
       ### Router
       
       This is an interface that manages routes. If one of the
       regexes matches the request path, the associated function is
       called. For example, a client requesting a path matching the
       regex ^/dice$ would receive a gophermap page containing the
       result of a dice roll.
       
       ### Server
       
       A server is essential since it is responsible for receiving
       Gopher requests from clients and responding to them. Before
       serving a file, it will ask its router if there is a routing
       rule for the path requested by the client. If there is one,
       the associated function will be called first to respond to
       the client.
       
       Note that before serving a directory, the server will check
       if there is a gophermap file at its root; if so, it will be
       served instead of the directory.
       
       Also, only files named gophermap and those with the
       .gophermap extension will be evaluated.
       
       ### Extension
       
       An extension is a pair consisting of a regex and a function;
       it is similar to the routes in the router component, except
       that here, an extension is specific to the gophermap format.
       Creating an extension allows you, for example, to add,
       modify, or delete gophermap item types. For instance, all
       standard RFC 1436 item types are implemented using
       extensions in the Fleur CLI.
 (HTM) RFC 1436
       
       ### Extension Manager
       
       This is an interface for managing extensions; it is used by
       the evaluator to extend the gophermap.
       
       ### Evaluator
       
       An evaluator evaluates gophermap files when the server is
       ready to serve them. This evaluation works line by line and
       allows the gophermap format to be extended by adding
       extensions.
       
       ### Example of a simple Gopher Application
       
       Below is a very simple example of a Gopher application
       designed to serve files.
       
       package main
       
       import (
           "flag"
           "log"
       
           "github.com/theobori/fleur/gopher"
           "github.com/theobori/fleur/gophermap/evaluator"
           "github.com/theobori/fleur/server"
       )
       
       func main() {
           var (
               err           error
               directoryPath string
               domain        string
               port          int
           )
       
           flag.StringVar(
               &directoryPath,
               "directory",
               "./fleur",
               "Root directory of the Gopher server",
           )
           flag.StringVar(
               &domain,
               "domain",
               "localhost",
               "Gopher domain",
           )
           flag.IntVar(
               &port,
               "port",
               gopher.DefaultPort,
               "Gopher port",
           )
       
           flag.Parse()
       
           serverOptions, err := server.NewOptions(
               port,
               directoryPath,
               domain,
               true,
           )
           if err != nil {
               log.Fatalln(err)
           }
       
           evaluatorOptions := evaluator.Options{
               Port:                 serverOptions.Port,
               DirectoryPath:        serverOptions.DirectoryPath,
               Domain:               serverOptions.Domain,
               EnableAutoInlineText: true,
           }
       
           em := evaluator.RFC1436ItemsExtensionManager()
           evaluator := evaluator.NewEvaluator(&evaluatorOptions, em)
           server := server.NewServer(serverOptions, evaluator)
       
           err = server.Serve()
           if err != nil {
               log.Fatalln(err)
           }
       }
       
       Once compiled, the application can be launched using the
       command line below.
       
       ./simple -port 7070 -directory "./"
       
       ## CLI
       
       The fleur CLI is a Gopher application that serves files
       implemented with the framework. It optionally supports
       personal Gopherspaces on UNIX systems, with the virtual path
       /~username/ being converted to /home/username/public_gopher.
       
       It also implements an extension that lists files in the
       current directory. To use it, type * on a line. This feature
       is inspired by gophernicus.
 (HTM) gophernicus
       
       ### Help Message
       
       Below is the CLI help message.
       
       Usage of ./fleur:
         -directory string
               It specifies an input directory path that will be the
       root of the Gopher server (default "./fleur")
         -domain string
               Gopher domain (default "localhost")
         -enable-auto-inline-text
               Relax non compliant text error and convert to gophermap
       inline text
         -enable-personal-gopherspaces
               Enable personal Gopherspaces, it allows each user of
       the system to serve its own files
         -port int
               Gopher port (default 70)
         -verbose
               Enable verbose logs
       
       ### NixOS module
       
       I also provide a NixOS module for managing the fleur CLI
       using a systemd service unit. Inside your flake.nix, you can
       start by adding the following lines.
       
       {
         inputs = {
           fleur.url = "github:theobori/fleur";
         };
       }
       
       You can then include the module and its default overlay in
       the system configuration as shown below.
       
       { inputs, ... }: {
         imports = with inputs; [ fleur.nixosModules.default ];
       
         services.fleur = {
           enable = true;
           directory = "${./my-directory}";
           port = 7070;
           verbose = true;
           autoInlineText = true;
           personalGopherspaces = true;
           # Other options
         };
       }
       
       ## Conclusion
       
       It was a fun project to write and test, and I plan to use it
       soon to serve my own gophermap files. Also, thanks to
       Wireshark, it was a huge help.
 (HTM) Wireshark