Skip to content

Latest commit

 

History

1,010 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Aperio

Put a local service on the public internet through one outbound connection. No inbound ports, no port-forwarding, no firewall holes. Self-hosted, written in Rust.

        Public request                        Outbound WebSocket tunnel
[ Visitor ] ────────────▶ [ aperio-server ] ◀═══════════════════════ [ aperio-client ]
                                 │                                          │
                                 ▼                                          ▼
                        Admin dashboard /aperio                     [ Local backend ]

The client always dials out, so nothing on your network accepts inbound connections.

Why Aperio

  • It is yours. Both sides are binaries you run: no account, no third-party relay, no traffic through someone else's infrastructure, nothing to price per tunnel or per seat.
  • Two binaries and a token. No database, no message broker, no sidecar. The server keeps its state in a file next to it; the dashboard is compiled into the binary.
  • One connection out. The tunnel is a WebSocket the client opens, so the machine serving your app can sit behind NAT, CGNAT or a firewall that allows nothing inbound.
  • Small enough to leave running. Measured on an Apple M-series laptop: the server binary is 14 MB (dashboard included) and idles at ~14 MB RSS; the client is 6 MB and idles at ~6 MB. Neither grows with request count.
  • It is a product, not a pipe. A live dashboard, a request inspector with replay, scoped tokens, organizations, caching, failover, autoscaling hooks and messaging between clients ship in the same binaries.

The Aperio admin dashboard

Quick start

# Server (public box)
docker run -d -p 8080:8080 -v ./data:/app/data \
  -e APERIO_SERVER_TOKEN="a-long-random-string" \
  ghcr.io/co3moz/aperio-server:latest

# Client (next to your service)
docker run -d --network host \
  -e APERIO_SERVER_TOKEN="a-long-random-string" \
  -e APERIO_SERVER_URL="http://your-server-ip:8080" \
  -e APERIO_TARGET="http://localhost:3000" \
  ghcr.io/co3moz/aperio-client:latest

Or one line with the CLI:

curl -sSf https://raw.githubusercontent.com/co3moz/aperio/master/install.sh | sh
aperio-client 3000 --server-url https://tunnel.example.com --server-token apr_xxxx

With Homebrew, or Scoop on Windows:

brew install --formula https://github.com/co3moz/aperio/releases/latest/download/aperio-client.rb

On an ordinary Linux box, a package with a hardened service unit:

sudo dpkg -i aperio-client_0.9.0_amd64.deb    # or rpm -i, both attached to every release
sudo cp /etc/aperio/aperio-client.yaml.example /etc/aperio/myapp.yaml
sudo systemctl enable --now aperio-client@myapp

See Native packages and service units.

Dashboard at /aperio (user aperio, password = your token). Full walkthrough: Getting Started.

What it carries

Supported
HTTP HTTP/1.1 and HTTP/2 (h2c and h2), streamed request and response bodies, Range requests, trailers
WebSocket passed through end to end, with per-stream flow control
gRPC over h2c/h2, te: trailers forwarded, grpc-status relayed back
TCP declared tunnels bound from another client (--bind-tunnels) or opened on a public port (expose:)
UDP the same, as a best-effort datagram relay; tcp/udp declares one tunnel on both
Static files a directory served with no backend at all: SPA fallback, custom 404, streaming
Messages clients of one organization publish and subscribe over the tunnel they already hold, with an MQTT or plain-HTTP local face

What it does with it

Routing hostname and path binds, round-robin, primary-standby tiers, sticky sessions, random subdomains, in-flight failover
Access scoped dynamic tokens (rate limits, quotas, IP pinning, expiry), visitor passwords, OIDC/SSO, share links, TOTP and passkeys for operators
Tenancy organizations with their own clients, tokens, users, hostname fences and quotas, and a super-admin who moves between them
Traffic control response caching with serve-stale, per-route rate limits, a small request firewall (waf:), maintenance mode, static routes and redirects
Operations live dashboard, request inspector with replay and cURL/HAR export, kill switch, config hot-reload, graceful drain, autoscaling hooks
Observability Prometheus metrics, OpenTelemetry traces, structured access log, tamper-evident audit trail, webhooks with retries and an inbox
Hardening end-to-end encrypted tunnels the server only relays, admin IP fencing, login lockout, token pinning, canary tokens, SSRF fencing on outbound callbacks

Throughput is not the interesting number for most deployments, the tunnel adds one hop, and the backend is usually what you are waiting for, but for scale: ~7,800 requests/second through the tunnel on loopback, with a trivial backend and one keep-alive connection, on the same laptop as above. Concurrency is where the number actually lives: the same setup serves ~22,000-25,000/second at a hundred connections, with the per-visitor rate limit raised out of the way (at its default it is the limiter you are measuring, not the tunnel). Both figures are floors rather than records, taken on a laptop with other work on it.

Use it for

  • Showing work in progress. A localhost port on a real URL, with TLS in front and a password or a share link on it.
  • Preview environments. One ephemeral hostname per pull request, minted through the API, torn down when it merges. There is a GitHub Action for the whole flow.
  • Publishing from where you cannot open ports. A machine behind NAT or CGNAT, a home server, a customer's network, a device in the field, serving traffic through an outbound connection.
  • Reaching a private service in an incident. A database or an SSH daemon that is exposed to nobody, bound locally from another machine for as long as you need it.
  • Running it as infrastructure for other people. Organizations, per-tenant quotas and hostname fences, per-token rate limits, and an audit trail of who did what.

Documentation

Click a feature for the details.

1. Getting started & configuration

2. Traffic & routing

3. Tunnels & protocols

  • Tunnel Protocol, WebSocket pass-through, chunked bodies, gRPC over h2c/h2.
  • Tunnels, reach a database, an SSH daemon or a DNS resolver through the same connection, end-to-end encrypted if you want it.
  • Messages Between Clients, publish a topic and every subscribed client of the organization hears it; the server's own events are on $aperio/.
  • PR Preview Tunnels, one ephemeral hostname per pull request, with a GitHub Action.

4. Security & access control

5. Management & operations

  • Admin Dashboard, live traffic, request inspector and replay, maintenance mode, kill switch.
  • Admin API from the CLI, script it all: aperio-client api share | token | ....
  • Autoscaling, cold start from zero on the first request, scale out when the pool saturates.
  • Multi-tenancy, isolated organizations on one server.

6. Observability

Full index: docs/. Prefer one long read? The Complete Guide is a single-file LaTeX book covering all of it in one narrative, with generated reference tables for every setting, endpoint and protocol message. Every release carries it built: aperio-guide.pdf.

Security

  • Front it with TLS, set trust_proxy, use https:// / wss:// URLs.
  • Prefer scoped dynamic tokens. Treat the master token like a root password.
  • The client only talks to its configured targets and caps message sizes.

More: Tokens & Authentication, and the Threat Model for what each side is trusted to do.

Releases are signed with Sigstore and carry build provenance and an SBOM, so a downloaded binary can be verified rather than trusted, see SECURITY.md. Found a vulnerability? Report it privately, never as a public issue.

Contributing

Bug reports, reproductions and documentation fixes are as welcome as code. CONTRIBUTING.md is the front door, docs/development.md the detail, and planned_features.md what is planned, shipped, or dropped and why.

License

Open-source and free to use.

About

Aperio is a secure, self-hosted reverse tunneling system written in Rust. It exposes HTTP services running behind NATs, firewalls, or private networks to the public internet

Resources

Contributing

Security policy

Stars

5 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages