Skip to content

someodd/venusia

Repository files navigation

O Venezia, Venaga, Venusia

Venusia is a fast and flexible Haskell library and daemon for building servers for the Internet Gopher Protocol. It's designed for ease of use, powerful configuration, and extensibility.

It works well with other tools in its ecosystem:

  • Build: Use Bore for static site generation (phlogs, templates) for your gopherspace.
  • Rank: Use RYVM to rank files for search results.

Features

Venusia is a complete framework for creating modern Gopherholes.

As a Library

  • Declarative Routing: Easily define routes with support for wildcards.
  • Built-in Handlers: Ready-to-use handlers for directories and files.
  • .gophermap Support: Automatically serves .gophermap files for custom menus.
  • Menu Builder: An intuitive DSL for creating Gopher menus.

As a Daemon (venusia-exe)

  • TOML Configuration: Use a simple routes.toml to configure file servers and gateways.
  • Hot Reloading: Watches your directory and routes.toml, reloading automatically on changes with no downtime.
  • Process Gateways: Execute external commands (e.g., cowsay, figlet, curl) and serve their output.

Getting Started

Running the Daemon

The quickest way to start is with the venusia-exe daemon.

  1. Create a routes.toml file:

    This file configures your Gopher server's behavior.

    # routes.toml
    
    [[files]]
    selector = "/files/"
    path = "/home/user/gopherhole/"
    
    # Gateway to the 'cowsay' command.
    [[gateway]]
    selector = "/gateway/cowsay"
    command = "cowsay"
    arguments = ["$search"] # Takes a search query
    menu = true
    search = true
    wildcard = false

    TODO: You can also use wildcards--a neat feature, I need to show an example!

    I sugguest trying out using .lhs (Literate Haskell) scripts with something like:

    [[gateway]]
    selector = "/gateway/something"
    search   = false
    menu = false
    wildcard = false
    command  = "runghc"
    arguments = ["/var/gopher/output/example.lhs"]

    Note that for the above TOML config nothing is mapped to /, so to test try something like gopher://localhost/1/files/. Make sure to change the path in the example (for [[files]]) to a directory you wanna serve.

    You may also want to know in Gopher the root selector is actually blank "" and not "/".

  2. Run the watch command:

    Point the watcher to your directory, host, and port.

    venusia watch /path/to/your/gopherhole gopher.example.com 7070

    Your Gopher server is now live. Changes to your files or routes.toml will be reflected instantly.

What about search support?

You can use my ryvm software as a gateway like this, in your routes.toml:

[[gateway]]
selector = "/search"
search = true
wildcard = false
menu = false
command = "/var/gopher/source/search.sh"
arguments = ["$search"]

And here's the /var/gopher/source/search.sh (don't forget to chmod +X!):

#!/usr/bin/env bash
# search.sh <search> [HOST] [PORT]
s="$1"; h="${2:-gopher.someodd.zip}"; p="${3:-70}"
cd /var/gopher/output || exit 1

ryvm --ext-whitelist txt --make-relative . "$s" \
| awk -F'\t' -v h="$h" -v p="$p" '
function is_gophermap(path,   l,ok){
  ok=0
  if ((getline l < path) > 0) {
    sub(/\r$/,"",l)
    if (l ~ /^.{2,}\t[^\t]+\t[^\t]+\t[^\t]+$/) ok=1
  }
  close(path)
  return ok
}
{
  file=$1
  sel = ($2 && $2 != "") ? $2 : file
  score = $3
  snip = $4

  t = is_gophermap(file) ? "1" : "0"     # 1 = menu (gophermap), 0 = text
  printf "%s%s — %s [score %s]\t%s\t%s\t%s\r\n", t, sel, snip, score, file, h, p
}'

Using the Library

To build a custom server, use Venusia in your own Haskell project.

  1. Add the dependency in package.yaml:

    dependencies:
    - Venusia
  2. Create your server:

    -- app/Main.hs
    module Main (main) where
    
    import Venusia.Server
    import Venusia.MenuBuilder
    import Venusia.FileHandler
    import Venusia.SearchHandler
    import qualified Data.Text as T
    
    import Control.Concurrent.MVar
    import Data.Maybe (fromMaybe)
    
    host :: T.Text
    host = "localhost"
    
    port :: Int
    port = 7070
    
    -- Define server routes
    routes :: [Route]
    routes =
      [ on "/hello" $ \_ ->
          return $ TextResponse "Hello, gopher!\r\n"
    
      , onWildcard "/echo/*" $ \req ->
          pure $ TextResponse $ fromMaybe "Nothing." (req.reqWildcard)
    
      , onWildcard "/files/*" $ \req ->
          case req.reqWildcard of
            Just path ->
              serveDirectory host port "/var/gopher" "/files/" path Nothing
            Nothing ->
              pure $ TextResponse "No path provided."
      ]
    
    -- Main entry point
    main :: IO ()
    main = do
      -- Wrap routes in MVar because serveHotReload requires mutable route list
      routesVar <- newMVar routes
    
      -- Start the new hot-reloadable, streaming-safe server
      serveHotReload (show port) noMatchHandler routesVar

Debian Packages & systemd

For production, Venusia offers Debian packages with systemd support to run as a managed daemon.

After installing a package, you can edit the service configuration at /lib/systemd/system/venusia.service. For example, to integrate with bore, you could change the ExecStart line to:

ExecStart=/usr/bin/venusia watch /var/gopher/source gopher.someodd.zip 7071 "/usr/bin/bore build --source /var/gopher/source --output /var/gopher/output" 10000000

Then, reload the systemd daemon and restart the service:

sudo systemctl daemon-reload
sudo systemctl restart venusia.service

Configuration (routes.toml)

Configure the daemon by defining routes in routes.toml.

  • [[files]]: Serves static files.
    • selector: Gopher path prefix (e.g., /files/).
    • path: Local directory to serve.
  • [[gateway]]: Executes a shell command.
    • selector: Gopher path, can include a wildcard (*).
    • command: The command to run.
    • arguments: Command arguments. $search and $wildcard are replaced with user input.
    • wildcard: Set to true if the selector uses a wildcard.
    • menu: Set to true to format the command's output as a Gopher menu.

About

Haskell library for Internet Gopher Protocol servers

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published