Skip to content

Quick Start

Get up and running with Mototli in minutes. This guide covers three common scenarios.

Scenario 1: Browse Gopherspace

Use the CLI to explore existing gopher servers.

Step 1: Fetch a directory listing

mototli get gopher.floodgap.com

You'll see a formatted directory listing:

[DIR]  /gopher       Welcome to Floodgap
[TXT]  /gopher/glog  The Gopher Gazette
[DIR]  /v2           Veronica-2 Search
[INF]                ────────────────────
[INF]                Last updated: 2024-01

Step 2: Fetch a text file

mototli text gopher.floodgap.com /gopher/welcome

Step 3: Get Gopher+ attributes

mototli attrs gopher.floodgap.com /gopher

Verbose output

Add --verbose to see the raw protocol exchange:

mototli get gopher.floodgap.com --verbose


Scenario 2: Serve a Gopherhole

Host your own gopher content in under a minute.

Step 1: Create a content directory

mkdir my-gopherhole
cd my-gopherhole

Step 2: Add a welcome file

Create index.txt:

Welcome to my gopherhole!

This is served by Mototli.

Step 3: Start the server

mototli serve . --port 7070

You'll see:

Starting Gopher server...
  Document root: /path/to/my-gopherhole
  Listening on: localhost:7070
  Gopher+ enabled: Yes

Step 4: Browse your site

In another terminal:

mototli get localhost:7070

Default port

Gopher's default port is 70, which requires root privileges on Unix systems. Use --port 7070 or another high port for development.


Scenario 3: Use the Python Client

Integrate Gopher access into your Python applications.

Step 1: Create a script

Create browse.py:

import asyncio
from mototli.client import GopherClient

async def main():
    async with GopherClient(timeout=30.0) as client:
        # Fetch a directory listing
        response = await client.get("gopher.floodgap.com", "/")

        print("=== Directory Listing ===")
        for item in response.items:
            if item.item_type.is_informational:
                print(f"     {item.display_text}")
            else:
                print(f"[{item.item_type.value}] {item.display_text}")
                print(f"     -> {item.selector}")

asyncio.run(main())

Step 2: Run it

python browse.py

Step 3: Fetch text content

async def fetch_text():
    async with GopherClient() as client:
        response = await client.get_text("gopher.floodgap.com", "/gopher/welcome")
        print(response.content.decode())

asyncio.run(fetch_text())

Step 4: Handle binary files

async def fetch_binary():
    async with GopherClient() as client:
        response = await client.get_binary("gopher.floodgap.com", "/gopher/logo.gif")

        with open("logo.gif", "wb") as f:
            f.write(response.content)
        print(f"Saved {len(response.content)} bytes")

asyncio.run(fetch_binary())

Common Issues

Connection refused

Error: Connection refused to localhost:70

Solution: The server isn't running, or you need to specify the correct port:

mototli get localhost:7070

Timeout errors

Error: Connection timed out

Solution: The server may be slow or unreachable. Increase the timeout:

mototli get slow-server.example --timeout 60

Port permission denied

Error: Permission denied for port 70

Solution: Port 70 requires root privileges. Use a high port:

mototli serve . --port 7070

Choose based on your goals:

I want to host a gopherhole

  1. Your First Gopherhole - Complete server tutorial
  2. Configure Server - TOML configuration
  3. Setup CGI - Dynamic content

I want to build a gopher client

  1. Building a Client - Client tutorial
  2. Fetch Resources - Advanced fetching
  3. Client API - Full API reference

I want to understand the protocol

  1. Gopher Protocol - Protocol deep-dive
  2. Gopher+ Extensions - RFC 4266
  3. Item Types - Type reference

Getting Help