# gopher-web-bridge A small **gateway** for Gopher and Gemini clients to read the web: you give it an HTTP or HTTPS URL, it fetches the page and converts the result into **Gopher menu** or **Gemini (gemtext)** so native clients can browse HTML-ish content without a full browser. Design goals: - Lynx-like behavior, but **output is Gopher or Gemini**, not a TTY UI. - **HTTP and HTTPS** URLs; links are emitted as **http(s)** targets (Gopher **`h`** + `URL:` lines; Gemini **`=>`** lines), resolved relative to the final URL after redirects. Optional **`-bridge-gemini`** / **`-bridge-gopher`** rewrite those links so they point at **your** gateway again (see `HOW-TO-SETUP-*.md`). Gopher bridging uses **`{selector}?{QueryEscape(url)}`** so path-based servers (e.g. **Gophernicus**) can **`stat`** the CGI script. - **Run on demand** (invoked by a client, script, or proxy hook), not necessarily as a long-lived network service. ## Requirements - Go (module root is **`go/`**). - [Task](https://taskfile.dev/) for repeatable builds (`task` CLI). ## Build `go.mod`, **`cmd/`**, and **`internal/`** live under **`go/`**. **Task** runs `go build` / `go test` from that directory with **`CGO_ENABLED=0`** (on typical **Linux**, a fully **static** ELF with no dynamic libc) and writes **`dist/gopher-web-bridge`** at the repository root. Static binary (no libc dynamic link on typical Linux): ```bash ./build.sh ``` Or directly: ```bash task build ``` Run tests: ```bash task test # or chmod +x test.sh && ./test.sh ``` From **`go/`** only (without Task); **`CGO_ENABLED=0`** avoids dynamic libc / libcgo so the Linux binary is **fully static** (pure Go): ```bash cd go export CGO_ENABLED=0 go build -trimpath -ldflags="-s -w" -o ../dist/gopher-web-bridge ./cmd/gopher-web-bridge go test ./... -count=1 ``` Try a **live** URL (needs network; builds `dist/` if the binary is missing or older than any `go/**/*.go`): ```bash chmod +x run.sh ./run.sh -format gemini https://example.com/ ./run.sh -f gopher https://example.com/ ``` The binary is written to `dist/gopher-web-bridge` (and `dist/README.txt` with a one-line deploy hint). ## Install Copy `dist/gopher-web-bridge` to a directory on your `PATH`, for example: ```bash install -m 0755 dist/gopher-web-bridge ~/.local/bin/ ``` ## Use ```text gopher-web-bridge help gopher-web-bridge -format gopher gopher-web-bridge -format gemini gopher-web-bridge -f gemini gopher-web-bridge -cgi gopher gopher-web-bridge -cgi gemini ``` Full usage text is shipped as **`go/internal/proxy/help.txt`** and **embedded** in the binary; `help` prints that file (not hard-coded strings in Go). Wrong or incomplete flags print the same text to stderr. `-format` or `-f` is required: `gopher` or `gemini`. The URL must use an `http://` or `https://` scheme (scheme matching is case-insensitive). Optional **link bridge** (so converted pages link back through your capsule instead of raw `https:`): - **`-bridge-gemini URI`** — when emitting gemtext, rewrite `http(s)` link targets to this Gemini base; the target URL is appended after **`?`** (or `?url=` / `&u=` depending on the base—see **`HOW-TO-SETUP-GEMINI.md`** §6). Prefer a base ending with **`?`** (e.g. `gemini://your.host/cgi/gwp?`). - **`-bridge-gopher URI`** — when emitting a Gopher menu, rewrite `http(s)` links as **type `1`** lines to your server; the selector is **`{prefix}?{QueryEscape(url)}`** so Gophernicus can **`stat`** the CGI script (see **`HOW-TO-SETUP-GOPHER.md`**). Examples: ```bash ./run.sh -f gopher -bridge-gopher 'gopher://g.example.org:70/1/cgi-bin/gwp' https://example.com/ ./run.sh -f gemini -bridge-gemini 'gemini://g.example.org/cgi-bin/gwp?' https://example.com/ ``` Output is written to **stdout**; errors to **stderr**. Exit code is non-zero on fetch/parse errors. The tool **GETs** the URL (HTTP or HTTPS, following redirects), parses **HTML**, and emits a simplified view: headings, paragraphs, lists, blockquotes, `pre`, and **http(s)** links from `a[href]` / `img[src]` (resolved relative to the final URL after redirects). Heavy pages are capped at **6 MiB** of response body. Real sites often include cookie banners and survey text in the DOM; output quality will depend on how clean the HTML is. ## CGI deployment Point your Gopher or Gemini server at the **same static binary** you use on the CLI, with **`-cgi gopher`** or **`-cgi gemini`** on the command line: ```text /opt/bin/gopher-web-bridge -cgi gopher /opt/bin/gopher-web-bridge -cgi gemini ``` The binary reads the environment variables described in **`HOW-TO-SETUP-GOPHER.md`** and **`HOW-TO-SETUP-GEMINI.md`**. **Gopher (Gophernicus):** the usual pattern is a **shell wrapper** in `cgi-bin/` that exports **`GOPHER_BRIDGE`** / **`SELECTOR_PREFIX`** and **`exec`**s the binary with **`-cgi gopher`**, because the server runs that file by path and does not attach custom argv. Copy and edit **`wrapper/gopher/gwp`**. **Gemini (gemserv):** you normally configure the **full command** and **env** in gemserv; a wrapper is **optional**. See **`wrapper/gemini/gwp`** if you want the same “env in a script” style. **Gemini (MoonGem):** there is no CGI **`QUERY_STRING`** contract; run **`gopher-web-bridge -f gemini`** from **Lua** (e.g. **`io.popen`**) as in **`HOW-TO-SETUP-GEMINI.md`** and **`wrapper/gemini/moongem-index.gmi`**. **If your server can only execute a pathname with no arguments** and does not run shell scripts, configure argv support if the server has it, or keep using a **`#!/bin/sh`** wrapper as above. ## Security notes (SSRF hardening) When used as a Gopher/Gemini gateway for untrusted users, fetching arbitrary URLs can enable SSRF. This tool blocks requests (and redirect targets) whose host resolves to **loopback**, **private**, or **link-local** IP ranges, plus other non-routable/special ranges. For development/tests only, you can temporarily allow these destinations by setting `GWP_ALLOW_PRIVATE=1`. ## Repository layout | Path | Purpose | |------|---------| | `go/` | Go module: `go.mod`, `cmd/gopher-web-bridge`, `internal/proxy`. | | `build.sh` | Thin wrapper: runs `task build`. | | `test.sh` | Runs `task test` (or `go test` / `go vet` in `go/`) plus `go vet`. | | `run.sh` | Builds if needed, then runs the proxy against a URL (network). | | `Taskfile.yml` | Build, static link, populate `dist/` (runs `go` commands in `go/`). | | `dist/` | Deployable artifacts (gitignored). | | `./` | **`HOW-TO-SETUP-GOPHER.md`**, **`HOW-TO-SETUP-GEMINI.md`** — server setup, environment variables, link bridging. | | `wrapper/` | Example **`gopher/gwp`** and optional **`gemini/gwp`** shell CGI wrappers (**`wrapper/README.md`**). | ## License GNU General Public License v3.0 (GPLv3). See `LICENSE`.