No description
  • Go 75.5%
  • HTML 16.2%
  • CSS 7.9%
  • Dockerfile 0.4%
Find a file
2026-04-19 11:00:34 +08:00
config Update config.go 2026-04-19 10:33:46 +08:00
handlers Update handlers.go 2026-04-19 10:43:56 +08:00
middleware Fix SSRF, IP spoofing, favicon leak, Windows-1252 decoding; add .env.example 2026-04-10 02:47:43 +00:00
protocols Update base.go 2026-04-19 10:51:17 +08:00
proxy Fix .env config loading, broken icons, security gaps, and missing features 2026-04-10 07:28:00 +00:00
static Fix white edges: move data-theme to html and set edge-to-edge bg 2026-04-10 06:45:47 +00:00
templates Update base.html 2026-04-19 10:35:07 +08:00
.env.example Update .env.example 2026-04-19 10:35:25 +08:00
.gitignore I love Ubuntu BTW 2026-04-08 13:54:26 +08:00
CHANGELOG.md Remove Mastodon features, fix theme CSS, and add branding configuration 2026-04-10 06:31:28 +00:00
docker-compose.yml Update docker-compose.yml 2026-04-19 11:00:34 +08:00
Dockerfile Update Dockerfile 2026-04-10 13:15:18 +08:00
go.mod I love Ubuntu BTW 2026-04-08 13:54:26 +08:00
main.go Update main.go 2026-04-19 10:44:29 +08:00
README.md Remove Mastodon features, fix theme CSS, and add branding configuration 2026-04-10 06:31:28 +00:00

Smolnet Portal (Go)

License: CC0 Go Version Build Status

A modern web proxy for the small internet — bringing Gemini, Gopher, Spartan, Finger, Nex, Scroll, and Text protocols to your browser over HTTP(S).

This is a high-performance Go reimplementation of Smolnet Portal (originally Python/Quart by Michael Lazar).

Features

Protocol Support

  • Gemini — rich text with inline styling
  • Gopher — classic menu browser with Gopher+ support
  • Gophers — secure gopher over TLS
  • Spartan — lightweight text protocol
  • Finger — user info protocol
  • Nex — directory listings
  • Scroll — modern markup with metadata
  • Text — plain text with smart URL linking

Content Processing

  • Gemtext → HTML rendering (headers, links, lists, preformatted blocks, blockquotes, inline emoji)
  • Gopher menu rendering with type icons and Gopher+ views/admin blocks
  • Scroll markup support (*bold*, _italic_, `code`) with metadata extraction
  • Nex directory listings with automatic link detection
  • Plain text auto-linking for URLs, emails, and protocols
  • Emoji favicons cached per domain

User Experience

  • Streaming support for large responses (>1 MiB)
  • Reader mode for distraction-free reading
  • Theme switcher (light/dark/sepia) with system preference detection
  • Font switcher (serif/sans-serif/monospace)
  • TLS certificate inspection for Gemini/Scroll
  • Inline markup with proper character preservation
  • Responsive design for mobile and desktop

Security & Protection

  • Rate limiting (10 req/s per IP, configurable)
  • Cookie-based CAPTCHA for bot prevention
  • Blocked hosts list with exact domain matching
  • Configurable TLS verification for development/testing
  • Secure port restrictions (default: ports 70, 77, 79, 300-301, 1900, 3000-3333, 5699, 7000-7099, 8070, 1960-2020)

Developer Features

  • Single static binary — no dependencies, no database
  • Configurable via environment variables — easy Docker/K8s deployment
  • Rebrandable — customize app names and descriptions via environment variables
  • Fail2ban integration via hidden trap link
  • Docker support with multi-stage build
  • Clean codebase with error logging

Installation

From Source

Requirements: Go 1.22+

# Clone the repository
git clone https://github.com/kalvin0x8d0/small-web-gateway.git
cd small-web-gateway

# Build the binary
go build -o smolnet-portal .

# Run it
PORT=8080 BASE_URL=http://localhost:8080 ./smolnet-portal

The proxy will be available at http://localhost:8080.

Using Docker

# Build and run
docker compose up --build

# Or with custom settings
docker run -e PORT=8080 -e BASE_URL=http://localhost:8080 \
  -p 8080:8080 smolnet-portal:latest

Binary Release

Download pre-built binaries from releases.

# Extract and run
tar xzf smolnet-portal-linux-amd64.tar.gz
PORT=8080 ./smolnet-portal

Configuration

Environment Variables

Variable Default Example Description
PORT 8080 8080 HTTP listen port
BASE_URL http://localhost:8080 https://proxy.example.com Public URL of the portal (for links)
APP_NAME Smolnet Portal My Portal Application name (page title)
APP_TITLE SMOLNET PORTAL MY PORTAL Application title (header)
SITE_DESCRIPTION (see config) Free proxy service Home page site description
WELCOME_GREETING Welcome, voyager! Hello there! Home page welcome message
CAPTCHA_TITLE Smolnet Captcha! Security Check Captcha page title
CAPTCHA_ENABLED true false Enable cookie CAPTCHA
BLOCKED_HOSTS vger.cloud,warpengineer.space,... site1.com,site2.com Blocked hostnames (comma-separated)
ALLOWED_PORTS (see config) 70,79,1965 Allowed ports (comma-separated)
TLS_INSECURE false true Skip TLS certificate verification (for development)

Docker Compose Example

version: '3.8'
services:
  smolnet:
    build: .
    ports:
      - "8080:8080"
    environment:
      PORT: "8080"
      BASE_URL: "http://localhost:8080"
      CAPTCHA_ENABLED: "true"
      BLOCKED_HOSTS: "vger.cloud,warpengineer.space"
      TLS_INSECURE: "false"
    restart: unless-stopped

Advanced Configuration

Disable CAPTCHA for local development:

CAPTCHA_ENABLED=false ./smolnet-portal

Allow additional ports:

ALLOWED_PORTS=70,79,1965,9000,9001 ./smolnet-portal

Disable TLS verification (development only):

TLS_INSECURE=true ./smolnet-portal

Usage

Accessing Content

The proxy rewrites URLs to its own format:

http://localhost:8080/gemini/geminiprotocol.net/
http://localhost:8080/gopher/gopher.floodgap.com:70/
http://localhost:8080/spartan/spartan.mozz.us/
http://localhost:8080/text/www.example.com/robots.txt

Query Parameters

Parameter Purpose Example
?raw=1 View raw response (no HTML rendering) /gemini/site.com/?raw=1
?crt=1 Display TLS certificate details /gemini/site.com/?crt=1
?raw_crt=1 Download x509 certificate /gemini/site.com/?raw_crt=1
?reader=1 Reader mode (minimal styling) /gemini/site.com/?reader=1
?meta=1 Show Scroll metadata /scroll/site.com/?meta=1
?vr=1 Virtual reality mode (gopher) /gopher/site.com/?vr=1
?charset=utf-8 Override character encoding /text/site.com/?charset=iso-8859-1
?lang=fr Set language preference /scroll/site.com/?lang=fr

Common URLs

Exploring Gemini:

http://localhost:8080/gemini/geminiprotocol.net/
http://localhost:8080/gemini/gmi.skyjake.fi/
http://localhost:8080/gemini/warmedal.se/

Browsing Gopher:

http://localhost:8080/gopher/gopher.floodgap.com:70/
http://localhost:8080/gopher/sdf.org:70/

Exploring Spartan:

http://localhost:8080/spartan/spartan.mozz.us/

Security Considerations

TLS/SSL

By default, the portal verifies TLS certificates. For development with self-signed certificates, use:

TLS_INSECURE=true ./smolnet-portal

⚠️ Never use TLS_INSECURE=true in production — it makes you vulnerable to MITM attacks.

Port Restrictions

The proxy only allows connections to specific ports to prevent scanning/abuse:

  • Default allowed: 70, 77, 79, 300-301, 1900, 3000-3333, 5699, 7000-7099, 8070, 1960-2020
  • Override with ALLOWED_PORTS=70,79,1965

Rate Limiting

Built-in rate limiting (10 requests/second per IP) prevents DoS abuse. IPs with inactive connections are auto-cleaned every 5 minutes.

Blocked Hosts

Configure a list of blocked hosts to prevent access to private/local networks:

BLOCKED_HOSTS=vger.cloud,warpengineer.space,localhost,127.0.0.1 ./smolnet-portal

Blocking works with:

  • Exact domain matching: example.com blocks example.com
  • Subdomain matching: example.com blocks sub.example.com

CAPTCHA Protection

A cookie-based CAPTCHA is enabled by default to protect against bot abuse. Disable with CAPTCHA_ENABLED=false.

Troubleshooting

Connection Refused

Issue: unable to establish connection with host

Causes:

  • Host is unreachable or offline
  • Port is not in the allowed list
  • Firewall blocking the connection

Solution:

# Check if host is reachable
ping geminiprotocol.net

# Verify port is allowed
ALLOWED_PORTS=70,79,1965,3333 ./smolnet-portal

SSL Certificate Errors

Issue: x509: certificate signed by unknown authority

Causes:

  • Self-signed certificate
  • Invalid/expired certificate
  • TLS verification enabled

Solution (development only):

TLS_INSECURE=true ./smolnet-portal

Host Blocked

Issue: this host has kindly requested that their content not be accessed via web proxy

Causes:

  • Host is in BLOCKED_HOSTS list
  • Host is on the default blocklist (vger.cloud, etc.)

Solution: Remove the host from BLOCKED_HOSTS if you manage it:

BLOCKED_HOSTS=other-site.com ./smolnet-portal

CAPTCHA Loop

Issue: Stuck on captcha screen

Causes:

  • Cookies disabled in browser
  • Cookie domain mismatch
  • BASE_URL incorrect

Solution:

  • Enable cookies in browser
  • Set correct BASE_URL: BASE_URL=https://proxy.example.com ./smolnet-portal
  • Disable CAPTCHA for testing: CAPTCHA_ENABLED=false ./smolnet-portal

Character Encoding Issues

Issue: Mojibake or corrupted text

Causes:

  • Wrong character encoding detected
  • Server not specifying charset

Solution:

# Override encoding
http://localhost:8080/text/site.com/?charset=iso-8859-1
http://localhost:8080/text/site.com/?charset=windows-1252

Contributing

Reporting Bugs

Found a bug? Please open an issue with:

  • Clear description of the problem
  • Steps to reproduce
  • Expected vs. actual behavior
  • Server logs (if relevant)

Submitting Changes

We welcome pull requests! Please:

  1. Fork the repository
  2. Create a feature branch: git checkout -b feature/your-feature
  3. Write clean, well-commented code
  4. Test your changes: go test ./...
  5. Submit a pull request with description

Code Quality

  • Run go fmt ./... before committing
  • Run go vet ./... to check for issues
  • Write tests for new features
  • Keep commits atomic and descriptive

Project Structure

.
├── main.go                 # HTTP handler and server setup
├── config/                 # Configuration loading
├── handlers/               # Protocol response conversion to HTML
├── protocols/              # Protocol clients (gemini, gopher, etc.)
├── proxy/                  # URL parsing and utilities
├── middleware/             # HTTP middleware (rate limiting, etc.)
├── templates/              # HTML templates
├── static/                 # CSS and static assets
└── docker-compose.yml      # Docker deployment

Performance

  • Single binary — ~20 MB executable
  • Minimal memory — <50 MB at rest, scales with concurrent users
  • Streaming — handles large responses without buffering
  • Rate limiting — prevents resource exhaustion
  • Caching — emoji favicons cached per domain

License

CC0 — Public Domain

This project is released into the public domain. You are free to use, modify, and distribute it without restriction.

Original Smolnet Portal by Michael Lazar under the Human Software License.

Acknowledgments

  • Michael Lazar — Original Python/Quart implementation
  • Gemini Protocol — Small internet pioneers
  • Gopher Community — For keeping the internet weird and small
  • Contributors — Bug fixes, features, and improvements

Further Reading


Made with ❤️ for the small internet