MCPcopy Index your code
hub / github.com/a-h/gemini

github.com/a-h/gemini @v0.0.69

Chat with this repo
repository ↗ · DeepWiki ↗ · release v0.0.69 ↗ · + Follow
126 symbols 385 edges 18 files 57 documented · 45%
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

Gemini

Applications and libraries for building applications on Gemini (see https://gemini.circumlunar.space/).

Gemini CLI

Run a server

gemini serve --domain=example.com --certFile=a.crt --keyFile=a.key --path=.

Request content

curl for Gemini.

gemini request --insecure --verbose gemini://example.com/pass

Gemini Server Docker image

Run a server with Docker

docker run \
    -v /path_to_your_cert_files:/certs \
    -e PORT=1965 \
    -e DOMAIN=localhost \
    -v /path_to_your_content:/content \
    -p 1965:1965 \
    adrianhesketh/gemini:latest

Quick start

Check out https://github.com/a-h/gemini/releases for the latest version of the gemini command line tool to run locally, or use Docker:

# Create a server certificate.
openssl ecparam -genkey -name secp384r1 -out server.key
openssl req -new -x509 -sha256 -key server.key -out server.crt -days 3650
# Make a Gemini file.
mkdir content
echo "# Hello, World!" > content/index.gmi
# Run the container.
docker pull adrianhesketh/gemini:latest
docker run -v `pwd`:/certs -e PORT=1965 -e DOMAIN=localhost -v `pwd`/content:/content -p 1965:1965 adrianhesketh/gemini:latest

Libraries

Serve

Use gemini.Server / gemini.ListenAndServe to build your own custom servers.

Supports hosting multiple Gemini servers on a single IP address.

These are used to build a Gemini application that supports dynamic content.

package main

import (
    "context"
    "fmt"
    "log"

    "github.com/a-h/gemini"
    "github.com/a-h/gemini/mux"
)

func main() {
    // Create the handlers for a domain (a.gemini).
    okHandler := gemini.HandlerFunc(func(w gemini.ResponseWriter, r *gemini.Request) {
        w.Write([]byte("OK"))
    })

    helloHandler := gemini.HandlerFunc(func(w gemini.ResponseWriter, r *gemini.Request) {
        w.Write([]byte("# Hello, user!\n"))
        if r.Certificate.ID == "" {
            w.Write([]byte("You're not authenticated"))
            return
        }
        w.Write([]byte(fmt.Sprintf("Certificate: %v\n", r.Certificate.ID)))
    })

    // Create a router for gemini://a.gemini/require_cert and gemini://a.gemini/public
    routerA := mux.NewMux()
    // Let's make /require_cert require the client to be authenticated.
    routerA.AddRoute("/require_cert", gemini.RequireCertificateHandler(helloHandler, nil))
    routerA.AddRoute("/public", okHandler)

    // Create a file system handler gemini://b.gemini/{path}
    handlerB := gemini.FileSystemHandler(gemini.Dir("./content"))

    // Set up the domain handlers.
    ctx := context.Background()
    a, err := gemini.NewDomainHandler("a.gemini", "a.crt", "a.key", routerA)
    if err != nil {
        log.Fatal("error creating domain handler A:", err)
    }
    b, err := gemini.NewDomainHandler("b.gemini", "b.crt", "b.key", handlerB)
    if err != nil {
        log.Fatal("error creating domain handler B:", err)
    }

    // Start the server for two domains (a.gemini / b.gemini).
    err = gemini.ListenAndServe(ctx, ":1965", a, b)
    if err != nil {
        log.Fatal("error:", err)
    }
}

Route

Use github.com/a-h/gemini/mux to provide routing between Gemini handlers and extract variables from URL paths.

Built-in utility handlers

  • RequireCertificateHandler a handler that ensures that users present certificates.
  • FileSystemHandler to support hosting static content.

Gemini client

client := gemini.NewClient()

// Make a request to the server without accepting its certificate.
r, certificates, authenticated, ok, err := client.Request("gemini://a.gemini/require_cert")
if err != nil {
    log.Printf("Request failed: %v", err)
    return
}

Configure allowed server certificates for trust-on-first-use certificate support:

client.AddAlllowedCertificateForHost("a.gemini", "3082016c3081f3020900d4c7c9907518eb61300a06082a8648ce3d0403023020310b30090603550406130267623111300f06035504030c08612e67656d696e69301e170d3230303832303139303330335a170d3330303831383139303330335a3020310b30090603550406130267623111300f06035504030c08612e67656d696e693076301006072a8648ce3d020106052b8104002203620004ae5cabe01f708d8f9423725df49601e1a033a1b51eb73cd3a8a9853011346127cbfedb57c4bd14ad6000ccb2f748d32b2a2b817b1860781d937e7666680874876fb4a9a91c44e2cf8c9804d40f6e7122f6c92a1884b62bd9f0749cca4e12cfa8300a06082a8648ce3d0403020368003065023100ae447eb9455e9ca1f02f013390d2c4029a7f29732cf6e29787b53b6435904d622f47f3b1fbffe60a284dbd4cddd6ef580230518dcb0355d5c3d880357128972c630ca90a915f1eb417a7ea0e4518a72dfc8a76c9b50c51d56f6a6835c4dfa989b72be3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")

Tasks

test

Test the project.

go test ./... -short

test-integration

Integration test the project.

go test ./...

build

Build the CLI.

go build -o gemini ./cmd/main.go

build-docker

Build the Docker image.

docker build . -t adrianhesketh/gemini

build-snapshot

Build a snapshot release using goreleaser.

goreleaser build --snapshot --rm-dist

serve-local-tests

Run a local Gemini server.

echo add '127.0.0.1       a-h.gemini' to your /etc/hosts file
openssl ecparam -genkey -name secp384r1 -out server.key
openssl req -new -x509 -sha256 -key server.key -out server.crt -days 3650 -subj "/C=/ST=/L=/O=/OU=/CN=a-h.gemini"
go run ./cmd/main.go serve --domain=a-h.gemini --certFile=server.crt --keyFile=server.key --path=./tests

release

Push a release to Github.

if [ "${GITHUB_TOKEN}" == "" ]; then echo "Set the GITHUB_TOKEN environment variable"; fi
./push-tag.sh
goreleaser --clean

Extension points exported contracts — how you extend this code

Handler (Interface)
Handler of Gemini content. [1 implementers]
server.go
FileSystem (Interface)
A FileSystem implements access to a collection of named files. The elements in a file path are separated by slash ('/',
filesystem.go
Field (FuncType)
(no doc)
log/log.go
ResponseWriter (Interface)
ResponseWriter used by handlers to send a response to the client. [1 implementers]
server.go
File (Interface)
A File is returned by a FileSystem's Open method and can be served by the FileServer implementation. The methods should
filesystem.go
HandlerFunc (FuncType)
HandlerFunc handles a Gemini request and returns a response.
server.go

Core symbols most depended-on inside this repo

String
called by 30
log/log.go
String
called by 23
mux/segment.go
SetHeader
called by 19
server.go
ServeGemini
called by 12
server.go
Write
called by 11
server.go
Error
called by 9
log/log.go
Info
called by 8
log/log.go
Request
called by 5
client.go

Shape

Function 59
Method 40
Struct 18
Interface 4
TypeAlias 3
FuncType 2

Languages

Go100%

Modules by API surface

server.go28 symbols
client.go21 symbols
server_test.go13 symbols
log/log.go10 symbols
handlers.go10 symbols
filesystem.go10 symbols
cmd/main.go10 symbols
mux/mux.go8 symbols
mux/segment.go3 symbols
mux/route.go3 symbols
mux/mux_test.go2 symbols
filesystem_test.go2 symbols

For agents

$ claude mcp add gemini \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact