github.com/watzon/sellia

HTTP tunneling made fast and easy

12 stars
0 dependents
License: MIT

Installation

# Add this to your shard.yml
dependencies:
  sellia:
    github: watzon/sellia
    version: ~> 0.3.1

Then run:

shards install

shard.yml

Crystal
>= 1.10.0
License
MIT
Author
Chris Watson <chris@watzon.me>
Targets
  • sellia from src/cli/main.cr
  • sellia-server from src/server/main.cr

Dependencies

Runtime Dependencies

README

# Sellia

[![Crystal](https://img.shields.io/badge/crystal-%3E%3D1.10.0-black?logo=crystal)](https://crystal-lang.org)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)

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](https://sellia.me). Named after the Crystal Tunnel in Elden Ring.

## Table of Contents

- [Background](#background)
- [Install](#install)
- [Usage](#usage)
  - [Quick Start](#quick-start)
  - [CLI Reference](#cli-reference)
  - [Server](#server)
  - [Configuration](#configuration)
- [Request Inspector](#request-inspector)
- [Development](#development)
- [Contributing](#contributing)
- [License](#license)

## 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](https://crystal-lang.org/install/) >= 1.10.0.

```bash
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):

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

**2. Create a tunnel** to your local server:

```bash
./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:**

```bash
# 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`:**

```yaml
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:

```bash
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:

```bash
# 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](https://cloudflare.com) (free tier works)
2. Create an [API token](https://dash.cloudflare.com/profile/api-tokens) 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

- [Crystal](https://crystal-lang.org/install/) >= 1.10.0
- [Node.js](https://nodejs.org/) >= 18 (for inspector UI development)

### Building

```bash
# Install dependencies
shards install

# Build debug binaries
shards build

# Build release binaries
shards build --release

# Run tests
crystal spec
```

### Inspector UI Development

```bash
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](https://www.conventionalcommits.org/) for commit messages.

## License

[MIT](LICENSE) © Chris Watson