package

github.com/watzon/sellia

0.3.1 / published Dec 29, 2025 / repository

HTTP tunneling made fast and easy

Sellia

Crystal License: MIT

Secure tunnels to localhost. A self-hosted ngrok alternative written in Crystal.

Sellia exposes your local development servers to the internet through secure tunnels. Run your own tunnel server or use the hosted service at sellia.me. Named after the Crystal Tunnel in Elden Ring.

Table of Contents

Background

Exposing local servers to the internet is essential for:

  • Webhook development (Stripe, GitHub, etc.)
  • Mobile app development against local APIs
  • Sharing work-in-progress with clients
  • Testing OAuth callbacks

Existing solutions like ngrok are excellent but can be expensive for individual developers. Sellia provides a fully open-source, self-hostable alternative with a familiar interface.

Features:

  • Subdomain-based routing (myapp.your-domain.com)
  • Real-time request inspector with web UI
  • Automatic reconnection with exponential backoff
  • Basic auth protection for tunnels
  • Rate limiting and subdomain validation
  • MessagePack-based binary protocol over WebSocket

Install

From Source

Requires Crystal >= 1.10.0.

git clone https://github.com/watzon/sellia.git
cd sellia
shards build --release

Binaries will be in ./bin/:

  • sellia - CLI client
  • sellia-server - Tunnel server

Pre-built Binaries

Coming soon.

Usage

Quick Start

1. Start the server (or use a hosted instance):

./bin/sellia-server --port 3000 --domain your-domain.com

2. Create a tunnel to your local server:

./bin/sellia http 8080 --server http://localhost:3000

3. Access your local server at the provided URL (e.g., http://abc123.your-domain.com:3000).

CLI Reference

sellia <command> [options]

Commands:
  http <port>     Create HTTP tunnel to local port
  start           Start tunnels from config file
  auth            Manage authentication
  version         Show version
  help            Show help

HTTP Options:
  -s, --subdomain NAME    Request specific subdomain
  -a, --auth USER:PASS    Enable basic auth protection
  -H, --host HOST         Local host (default: localhost)
  -k, --api-key KEY       API key for authentication
  -i, --inspector-port    Inspector UI port (default: 4040)
  -o, --open              Open inspector in browser
  --no-inspector          Disable the request inspector
  --server URL            Tunnel server URL

Examples:

# Basic tunnel
sellia http 3000

# Custom subdomain
sellia http 3000 --subdomain myapp

# With basic auth
sellia http 3000 --auth admin:secret

# Using a specific server
sellia http 3000 --server https://sellia.me

Server

sellia-server [options]

Options:
  --host HOST           Host to bind to (default: 0.0.0.0)
  --port PORT           Port to listen on (default: 3000)
  --domain DOMAIN       Base domain for subdomains
  --require-auth        Require API key authentication
  --master-key KEY      Master API key (enables auth)
  --https               Generate HTTPS URLs for tunnels
  --no-rate-limit       Disable rate limiting

Environment Variables:

| Variable | Description | | ---------------------- | --------------------------------------- | | SELLIA_HOST | Host to bind to | | SELLIA_PORT | Port to listen on | | SELLIA_DOMAIN | Base domain for subdomains | | SELLIA_REQUIRE_AUTH | Require authentication (true/false) | | SELLIA_MASTER_KEY | Master API key | | SELLIA_USE_HTTPS | Generate HTTPS URLs (true/false) | | SELLIA_RATE_LIMITING | Enable rate limiting (true/false) | | SELLIA_DEBUG | Enable debug logging (true/false) |

Configuration

Sellia supports layered configuration. Files are loaded in order (later overrides earlier):

  1. ~/.config/sellia/sellia.yml
  2. ~/.sellia.yml
  3. ./sellia.yml
  4. CLI flags

Example sellia.yml:

server: https://sellia.me
api_key: your-api-key

tunnels:
  web:
    port: 3000
    subdomain: myapp
  api:
    port: 8080
    subdomain: myapp-api
    auth: admin:secret

Start all configured tunnels:

sellia start

Request Inspector

Sellia includes a real-time request inspector accessible at http://localhost:4040 when a tunnel is running.

Features:

  • Live request/response streaming
  • Request details (headers, body, timing)
  • Copy as cURL command
  • Clear history

Disable with --no-inspector if not needed.

Deployment

Docker Compose

The easiest way to deploy Sellia is with Docker Compose:

# Clone and configure
git clone https://github.com/watzon/sellia.git
cd sellia

# Create .env file
cat > .env << EOF
SELLIA_DOMAIN=yourdomain.com
SELLIA_MASTER_KEY=$(openssl rand -hex 32)
SELLIA_REQUIRE_AUTH=true
SELLIA_USE_HTTPS=true
EOF

# Start the server
docker compose -f docker-compose.prod.yml up -d

TLS Configuration

Sellia uses Cloudflare DNS challenge to obtain wildcard certificates, providing instant HTTPS for all subdomains with no delay on first request.

Setup:

  1. Add your domain to Cloudflare (free tier works)
  2. Create an API token with Zone:DNS:Edit permission
  3. Add to .env: CLOUDFLARE_API_TOKEN=your-token-here
  4. Start/restart: docker compose -f docker-compose.prod.yml up -d

Note: If you prefer a different TLS approach, you can customize deploy/Caddyfile to use any method Caddy supports (on-demand TLS, manual certs, etc.).

Development

Prerequisites

Building

# Install dependencies
shards install

# Build debug binaries
shards build

# Build release binaries
shards build --release

# Run tests
crystal spec

Inspector UI Development

cd web
npm install
npm run dev

The CLI will proxy to Vite's dev server at localhost:5173 when not built with embedded assets.

Project Structure

src/
├── core/           # Shared protocol and types
│   └── protocol/   # MessagePack message definitions
├── server/         # Tunnel server components
│   ├── tunnel_registry.cr
│   ├── ws_gateway.cr
│   ├── http_ingress.cr
│   └── ...
└── cli/            # CLI client components
    ├── tunnel_client.cr
    ├── inspector.cr
    └── ...
web/                # React inspector UI
spec/               # Tests

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'feat: add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

Please use conventional commits for commit messages.

License

MIT © Chris Watson

API