ThwipHome

Configuration / HTTP

Servers & routing

A server declares an IP/port listener, an optional virtual-host name, and its routes.

Listener

[[http.servers]]
listen = "127.0.0.1:8080"
server_name = "example.test"

listen is required and must be a literal socket address. Use 127.0.0.1 for local access, 0.0.0.0 for all IPv4 interfaces, or an IPv6 form such as [::1]:8080. The name is optional.

Virtual hosts

[[http.servers]]
listen = "0.0.0.0:8080"
server_name = "www.example.test"

[[http.servers]]
listen = "0.0.0.0:8080"
server_name = "api.example.test"

Identical addresses share one listener. Thwip matches the request Host to server_name, ignoring ASCII case and the port. The first server in a listener group is the fallback, so place the desired default first.

SSL / TLS ingress

[[http.servers]]
listen = "0.0.0.0:443"
server_name = "example.test"
ssl = { certificate_path = "/etc/thwip/fullchain.pem", private_key_path = "/etc/thwip/privkey.pem", handshake_timeout_ms = 10000, protocols = ["tlsv1_2", "tlsv1_3"] }

Adding ssl makes this listener accept TLS rather than plaintext HTTP. Certificate and key paths must be readable PEM files. TLS 1.2 and TLS 1.3 are supported; omit protocols and ciphers to use the secure defaults.

SSL is optional. A server without an ssl block remains plaintext. One certificate is selected per listener today, so do not place multiple certificate-requiring virtual hosts on the same address until SNI support lands. HTTPS upstreams are also separate, unsupported work.

Matchers and precedence

[[http.servers.locations]]
matcher = { type = "exact", path = "/health" }
action = { type = "response", status = 200, body = "OK" }

[[http.servers.locations]]
matcher = { type = "prefix", path = "/api" }
action = { type = "proxy", upstream_group = "api" }

exact targets one path. prefix targets paths beginning with its value. Exact wins first; otherwise the longest prefix wins, so /api/admin beats /api.