runewarp 0.1.0

Runewarp is an ingress tunneling tool for exposing local services without moving TLS termination to the edge. Clients connect out over QUIC, so you can publish services without putting your backend directly on the Internet or leaking your public IP.
Documentation
# Usage

Use this guide for the operator workflow: install Runewarp, prepare trust material, start the runtime, and verify traffic. Use [`docs/configuration.md`](configuration.md) for the full key reference, validation rules, and alternate config shapes.

## Choose a path

| Path | Best for | Next step |
| --- | --- | --- |
| CLI | Running Runewarp directly on your own hosts or service manager | Follow [Operate the CLI]#operate-the-cli |
| Docker example | Proving the shipped topology end to end before adapting it | Follow [Evaluate with the Docker example]#evaluate-with-the-docker-example |

## Before you start

Runewarp assumes:

- a public **Server** reachable on its configured `server.public-bind-address` for **Visitor** TLS traffic and `server.tunnel-bind-address` for **Client** **Tunnel connections**
- one or more operator-owned **Public hostnames** that resolve to the Server
- a **Local backend** behind the Client
- a decision about the Server certificate path and, if any Service uses `tls-mode = "terminate"`, the Client certificate path for those **Public hostnames**

If the product language here feels unfamiliar, read [`CONTEXT.md`](../CONTEXT.md) first.

## Evaluate with the Docker example

The repository ships one concrete example under [`examples/docker/`](../examples/docker/). Use it when you want to prove the end-to-end path before writing your own config.

```bash
git clone https://github.com/runewarp/runewarp.git
cd runewarp/examples/docker
./prepare.sh
docker compose up -d
./smoke.sh
```

Use [`examples/docker/README.md`](../examples/docker/README.md) for the full walkthrough and reset flow.

## Operate the CLI

### 1. Install Runewarp

```bash
cargo install runewarp
```

Runewarp is one binary with role-specific subcommands:

```bash
runewarp --help
runewarp server --help
runewarp client --help
```

### 2. Choose the Server certificate path

Choose one of the supported Server-certificate paths:

| Path | When to use it | What to do |
| --- | --- | --- |
| ACME (Let's Encrypt) | Publicly routable **Server hostname** and standard public trust | Configure `[server.acme]` |
| Manual/private-CA | Private deployments or operator-managed trust | Create the material with `runewarp server cert init` and distribute `server-ca.crt` to Clients |

Manual/private-CA initialization:

```bash
runewarp server cert init --hostname tunnel.example.com
```

When `server.hostname` is already set in config, `runewarp server cert init` and `runewarp server cert rotate-ca` can omit `--hostname`.

### 3. Create the Client identity

Create the Client keypair, certificate, and durable `client-identity`:

```bash
runewarp client identity init
```

Read the generated `client-identity.txt` value and place it into the matching Server `[[server.tunnels]]` entry as `client-identity`.

To print only the fingerprint for scripts:

```bash
runewarp client identity show
```

### 4. If needed, choose the Client certificate path for terminate mode

Skip this step when every Service uses the default `tls-mode = "passthrough"`.

If any Service uses `tls-mode = "terminate"`, choose one of these paths:

| Path | When to use it | What to do |
| --- | --- | --- |
| Manual **Public hostname certificates** | Private deployments or operator-managed trust | Run `runewarp client public-cert init` and distribute `public-ca.crt` to Visitors |
| ACME (`[client.acme]`) | Publicly routable **Public hostnames** and standard public trust | Configure `[client.acme]` |

Manual initialization:

```bash
runewarp client public-cert init --hostname app.example.com
```

Or derive the terminating hostnames from the selected Client config:

```bash
runewarp client public-cert init
```

### 5. Write the smallest practical config

The smallest practical setup is a Server with explicit **Public hostnames** and one Client **Catch-all Service**:

```toml
# /etc/runewarp/server.toml
[server]
hostname = "tunnel.example.com"

[server.acme]
email = "admin@example.com"

[[server.tunnels]]
public-hostnames = ["app.example.com", "api.example.com"]
client-identity = "4f7b6f7a9b0f0d2b..."
```

```toml
# /etc/runewarp/client.toml
[client]
server-address = "tunnel.example.com"

[[client.services]]
backend-address = "localhost:8443"
```

That Client uses a **Catch-all Service**: the Server still owns explicit **Public hostname authorization**, while the sole Client Service forwards every admitted hostname to one **Local backend**.

If you are using the manual/private-CA Server path, add:

```toml
server-trust = "ca-file"
# optionally override the default CA bundle path:
# server-ca-file = "/etc/runewarp/server-ca.crt"
```

For exact-match Client routing, multiple Services or Tunnels, terminate-mode config, defaults, and full key definitions, use [`docs/configuration.md`](configuration.md).

### 6. Prepare DNS and deployment

Run the **Server** on a machine with a public IP so **Visitors** and **Client instances** can reach it.

- create an A record for the **Server hostname** that points to that machine's public IP
- use each **Public hostname** as a CNAME to the **Server hostname** as the standard DNS pattern
- if you deploy the Server behind NAT or port mapping, make sure public TCP 443 still reaches the Server for Visitor TLS and ACME traffic

Pointing a **Public hostname** directly at the same public IP can also work, but CNAME-to-Server-hostname keeps the public routing shape clearer in most deployments.

### 7. Start the runtime

```bash
runewarp server -c /etc/runewarp/server.toml
runewarp client -c /etc/runewarp/client.toml
```

If you omit `--config`, Runewarp looks for `$XDG_CONFIG_HOME/runewarp/config.toml`, then `~/.config/runewarp/config.toml` when `XDG_CONFIG_HOME` is unset. Use [`docs/configuration.md`](configuration.md) for the full config discovery and runtime override rules.

### 8. Verify traffic

1. Point each **Public hostname** at the Server.
2. Make a TLS request to the Public hostname.
3. Confirm the expected application answers.

Under **TLS passthrough**, the backend's own certificate should appear. In **Terminate mode**, the Client-presented **Public hostname certificate** should appear and the backend should receive plaintext.

At the default `log-level = "info"`, Runewarp logs readiness, tunnel connection lifecycle, warnings, and errors to stderr. Set `log-level = "debug"` when you need per-connection routing detail. Use [`docs/configuration.md`](configuration.md) for the exact logging semantics.

## Troubleshooting

| Symptom | Likely cause | What to check |
| --- | --- | --- |
| No traffic reaches the backend | No active **Tunnel connection** | Confirm the Client is running and can reach the Server on `server.tunnel-bind-address` |
| Client cannot connect to the Server | Wrong Server trust path | Check `client.server-trust` and `client.server-ca-file`, or confirm the ACME/public-CA chain is trusted |
| Server drops a Public hostname | No Server `[[server.tunnels]]` entry grants **Public hostname authorization** for it | Check `server.tunnels[].public-hostnames` |
| Client rejects the stream | No matching **Service** on the Client | Check Client `public-hostnames`, or confirm the sole Service is intentionally Catch-all |
| Passthrough backend handshake fails | Backend is not terminating TLS | Confirm the backend behind a passthrough Service speaks TLS |
| Terminate-mode backend fails immediately | Backend still expects TLS after the Client terminated it | Confirm the matching Service uses `tls-mode = "terminate"` and the backend speaks plaintext TCP |