RELIW Operations and Development Guide

Overiew

This guide documents the current RELIW behavior implemented in:

It is intended for both operators (deployment and troubleshooting) and developers (schema, routing, and failure semantics).

Architecture

RELIW uses a coroutine-per-connection model built on the LEV async I/O runtime. The manager process forks listener processes (IPv4, IPv6, metrics), and each listener runs a lev.run() event loop that accepts connections and spawns a coroutine per client. Redis access uses redis (async Redis client) injected into the request handler context. TLS with SNI is handled by LEV's LITLS integration.

Legacy config field names fork_limit and requests_per_fork are accepted as aliases for connection_limit and requests_per_connection.

1. Quick Start

1.1 Runtime entry point

The standalone RELIW app entry point is defined in buildgen/apps/reliw.lua and runs:

local reliw = require("reliw")
math.randomseed(os.time())
local reliw_srv, err = reliw.new()
if not reliw_srv then
	print("failed to init RELIW: " .. tostring(err))
	os.exit(-1)
end
reliw_srv:run()

1.2 Config file location

RELIW reads config JSON from:

  1. RELIW_CONFIG_FILE environment variable, if set

  2. /etc/reliw/config.json otherwise

1.3 Minimal HTTP config

{
  "ip": "127.0.0.1",
  "port": 8080
}

1.4 Minimal Redis data for one host

Example below serves / for example.com from data_dir/example.com/index.md.

redis-cli -n 13 SET RLW:API:example.com '[["/","home",true]]'
redis-cli -n 13 SET RLW:API:example.com:home '{"methods":{"GET":true,"HEAD":true},"index":"index.md"}'

Create content file:

mkdir -p /www/example.com
printf '# Hello\n' > /www/example.com/index.md

Request:

curl -H 'Host: example.com' http://127.0.0.1:8080/

1.5 Manager inspection API

The reliw.new() manager object exposes read-only inspection helpers:

These are intended for runtime introspection and tests, replacing direct reads of internal state tables.

2. Full Configuration Reference

RELIW combines defaults from src/reliw/reliw.lua and src/http/http/server.lua.

2.1 Top-level server config

KeyDefaultNotes
ip127.0.0.1IPv4 bind address
port8080Bind port
ipv6unsetIf set, manager spawns IPv6 server process
data_dir/wwwStatic/dynamic content root
cache_max_size5242880Max bytes for cached file content in Redis
backlog256Listen backlog
connection_limit64Max concurrent connections per listener process
requests_per_connection512Requests handled on one connection before close
max_body_size5242880Request body cap for content-length and chunked uploads
request_line_limit8192Max request line or header line bytes
keepalive_idle_timeout15Keep-alive idle timeout (seconds)
request_header_timeout10Header read timeout (seconds)
request_body_timeout30Body read timeout (seconds)
tls_handshake_timeout10Server-side TLS handshake timeout (seconds)
log_levelaccessLogger level passed to std.logger
log_headers["referer","x-real-ip","user-agent"]Request headers copied into access logs
compressionenabledResponse compression policy (gzip preferred, deflate fallback)
redisobjectRedis connection and namespace config
metricsobjectMetrics listener + SCAN tuning
sslunsetEnable HTTPS listener (see TLS section)

Compression defaults:

2.2 Redis config (redis)

KeyDefaultNotes
host127.0.0.1Redis host
port6379Redis port
db13Selected DB
prefixRLWKey namespace prefix
timeout5Socket timeout (seconds)
sslunsetOptional Redis TLS mode
authunsetOptional auth object for Redis AUTH

2.3 Metrics config (metrics)

KeyDefaultNotes
ip127.0.0.1Metrics listener bind IP
port9101Metrics listener bind port
disabledfalseIf true, metrics process is not spawned
scan_count100Redis SCAN count hint; clamped to 1..1000
scan_limit2000Max keys inspected per scrape; clamped to 1..10000

Metrics process behavior:

2.4 TLS config (ssl)

Server-side TLS config shape:

{
  "ssl": {
    "default": { "cert": "/path/default.crt", "key": "/path/default.key" },
    "hosts": {
      "*.example.org":  { "cert": "/path/_.example.org.crt",  "key": "/path/_.example.org.key" },
      "other-site.com": { "cert": "/path/other-site.com.crt", "key": "/path/other-site.com.key" }
    }
  }
}

Notes:

3. Redis Data Model

All keys are prefixed with redis.prefix (default RLW).

3.1 Routing and entry metadata

Common metadata fields:

3.2 Content and templates

3.3 Auth/session keys

3.4 Proxy metadata schema

3.5 WAF and control channels

3.6 Metrics and rate-limit keys

4. WAF Behavior

Rule document format (global or per-host):

{
  "ip_header": "x-forwarded-for",
  "query": ["^/admin", "drop%stable"],
  "headers": {
    "user-agent": ["badbot", "sqlmap"],
    "x-custom": ["evil"]
  }
}

Semantics:

5. Request Handling Semantics

Main handler: src/reliw/reliw/handle.lua.

Order of operations:

  1. Initialize Redis-backed store for request.

  2. Normalize/validate host and query.

  3. Verify host is a configured vhost (421 if not).

  4. Evaluate WAF.

  5. Check host-level proxy config.

  6. Resolve route metadata.

  7. Apply auth, method checks, and rate limits.

  8. Load/render content.

  9. Apply ETag/cache semantics and update metrics.

Client IP normalization at request ingress:

5.1 Host and query hardening

Host validation:

Query validation:

5.2 Auth flow

metadata.auth supports three paths:

5.3 Proxy routing behavior

If ${PREFIX}:PROXY:<host> exists, RELIW proxies request and skips local content flow.

Current proxy behavior:

5.4 ETag and method semantics

5.5 Content rendering

Markdown and Djot (text/markdown, text/djot):

Lua handlers (application/lua):

6. Failure-Mode Responses

Common status outcomes:

StatusTrigger
400Invalid host header or invalid query
421Request to non-configured vhost
401Login failure (wrong creds or malformed body)
404Route/content missing; non-/metrics on metrics listener
405Method not allowed by entry metadata
429Rate limit exceeded
500Metadata/content/Lua content execution failures
502Upstream proxy failures
503Store initialization failure (main handler or metrics handler)

Additional behavior:

7. Observability

7.1 Logs

Main listener and metrics process both emit structured logs via std.logger.

Access-style request logs include:

Important explicit log events:

7.2 Metrics endpoint

Metrics listener:

Exported families:

All metrics commands per request are batched into a single Redis pipeline.

8. Testing

RELIW regression tests are not yet implemented.