CLI
Standalone reverse / edge proxy for any backend stack. MIT licensed. For operators who want YAML (or a familiar reverse-proxy dialect) instead of embedding .NET.
Next: Download a self-contained binary → write a config → titanium test / titanium run. Alias: twp.
Quick start
schemaVersion: "7.1"
listeners:
- host: "127.0.0.1"
port: 8000
decryptSsl: false
forwardHost: "127.0.0.1"
forwardPort: 8080titanium test -c twp.yaml
titanium run -c twp.yamlForeground run blocks until Ctrl+C (or SIGTERM). Exit 0 on clean stop; 1 on config/start errors. Opt-in NDJSON access logs via server.accessLog (see Configuration); bodies are never buffered for logging.
Path-based routing and load balancing: Configuration.
Commands
titanium run -c <config> [-v|--verbose] [--watch] [--service]
titanium reload -c <config>
titanium restart -c <config> # Linux hot restart, needs server.hotRestart
titanium test -c <config>
titanium healthcheck (-c <config> | --url <url>) [--timeout <seconds>]
titanium service install|uninstall|start|stop|restart|status
titanium version [--check] [--plus] [--channel beta]
titanium update [--plus] [--remove-plus] [--channel beta]
titanium http3-deps status|install
titanium scripting-deps status|installNested help: titanium <command> --help (and titanium service install --help, etc.).
| Command | Purpose |
|---|---|
run | Start the proxy from a config file |
reload | Ask a running proxy to reload its config in place; see Reload without restart |
test | Validate config without serving traffic |
healthcheck | Probe the admin /healthz once (container HEALTHCHECK; exit 0 is healthy) |
service | Install / start / stop an OS service so the proxy survives reboot |
version | Print local version; --check compares to the update feed |
update | Self-update the CLI (download, verify SHA256, replace install); --plus installs/updates Plus; --remove-plus deletes it |
http3-deps | Report Quic availability; optionally install system MsQuic on edge hosts |
scripting-deps | Report whether the Lua and Wasmtime scripting natives load; install prints per-OS fixes |
run
titanium run -c <config> [-v|--verbose] [--watch] [--service] [--name <service-name>]| Flag | Meaning |
|---|---|
-c, --config | Path to config (required) |
-v, --verbose | Debug console logging |
--watch | Reload when the config (or a *File secret or certificate) changes, including Kubernetes ConfigMap and Secret ..data symlink swaps. See Kubernetes |
--service | Service-worker mode (used by titanium service install; no “Press Ctrl+C” prompt; SIGTERM / SCM stop) |
--name | Windows SCM name when --service is set (default titanium) |
test
titanium test -c <config>Loads and validates the config without opening listeners. Exit 0 when OK; 1 when validation fails.
healthcheck
titanium healthcheck -c <config>
titanium healthcheck --url http://127.0.0.1:9901/healthzWith -c, the host and port come from server.admin; a wildcard bind is probed on loopback. It exits 0 without probing when server.admin is off or uses port 0, so an image without an admin listener does not report unhealthy. /healthz needs no token.
service
Register the same titanium run binary with the OS so it starts at boot and restarts on failure (Windows Service, Linux systemd, or macOS launchd): one process, the OS supervises it.
titanium service install -c <config> [--name titanium] [--user] [--no-start]
titanium service uninstall [--name titanium] [--user]
titanium service start|stop|restart|status [--name titanium] [--user]| Flag | Meaning |
|---|---|
-c, --config | Config path for install (validated; stored as an absolute path) |
--name | Service / unit name (default titanium). On macOS the launchd label is com.justcoding121.<name> unless the name already starts with com. |
--user | Per-user systemd unit or LaunchAgent (no root). Not supported on Windows. Ports 80/443 usually fail without privileges. |
--no-start | Install and enable, but do not start immediately |
The unit runs:
titanium run -c <abs-config> --serviceWorking directory is the config file’s directory (so relative cert / static paths in YAML still resolve). Machine services require Administrator (Windows) or root (Linux/macOS). In an interactive terminal, Titanium asks the OS for permission (UAC on Windows, sudo on Linux/macOS). If you cancel the prompt, or the session is not interactive (CI / redirected IO), re-run from an elevated prompt — or use --user on Linux/macOS.
Examples
# Windows — UAC prompt if you are not already Administrator
titanium service install -c C:\proxy\twp.yaml
titanium service status
titanium service stop
titanium service start
# Linux (systemd) — sudo prompt if you are not root
titanium service install -c /etc/titanium/twp.yaml
# Per-user (no elevation); for start-at-boot without login:
# loginctl enable-linger $USER
titanium service install -c ~/twp.yaml --user
# macOS (LaunchDaemon) — sudo prompt if you are not root
titanium service install -c /usr/local/etc/titanium/twp.yamlLogs
| OS | Where to look |
|---|---|
| Windows | %ProgramData%\Titanium\logs\titanium.log when YAML has no file log (SCM has no console). Event Viewer for service start/stop. |
| Linux | journalctl -u titanium (system) or journalctl --user -u titanium (--user) |
| macOS | /Library/Logs/Titanium/ (daemon) or ~/Library/Logs/Titanium/ (--user) |
Status exit codes
titanium service status exits 0 when the unit is installed (running or stopped), 1 when not installed.
version
titanium version [--check] [--plus] [--channel stable|beta]Prints local CLI / Core / Abstractions / Configuration versions. With --check, compares to the update feed (0 up to date, 2 update available, 1 feed error). --plus includes Plus.
update
titanium update [--plus] [--remove-plus] [--channel stable|beta]Downloads the CLI zip (or Plus with --plus), verifies SHA256, and replaces the install. --remove-plus deletes Plus beside the CLI (no network; mutually exclusive with --plus). If an OS service is running, stop it first so the executable can be replaced:
titanium service stop
titanium update
titanium service startChannels: stable (default) or beta via --channel or TITANIUM_UPDATE_CHANNEL (other values are rejected). Messages always label the channel and print local → remote versions. titanium update installs when the feed build is newer than your install (SemVer-ish: 7.0.6-beta is newer than Stable 7.0.5, but same-core 7.0.5-beta is not newer than Stable 7.0.5). Switching channel to Stable at the same version promotes off beta. It does not reinstall when already current. titanium update uses the product download feed on every OS (not OS package managers).
http3-deps
titanium http3-deps status|installReports Quic support and optionally installs system MsQuic. Prefer the matching download zip, which already bundles natives — see HTTP/3.
scripting-deps
titanium scripting-deps status|installstatus reports whether the Lua (Titanium shim) and Wasmtime natives load (Lua: / Wasmtime: lines) and prints per-OS fixes; it exits 0 either way. install exits 0 when both load, otherwise prints the supported routes (matching RID zip, or tools/packaging/bundle-scripting-native.ps1) and exits 1 because nothing is installed through a system package manager.
Plus sidecar
titanium update --plus
titanium update --remove-plusEnable in config (plus.enabled: true + control-plane shared secret); disable with plus.enabled: false. Use --remove-plus to delete Plus from disk. Details: Plus.
More
Config dialects
| Extension | Dialect |
|---|---|
.yaml / .yml / .json | Native twp schema 7.1 |
.twp | Compact site-file (host / => http://origin) |
.conf | HTTP-server style (listen, server_name, location, proxy_pass) for familiar reverse-proxy configs |
Alpine / containers
Download the linux-musl-* zip (not linux-x64) for Alpine or musl-based Kubernetes images. See HTTP/3.
Reload without restart
Change routes, clusters, and server: knobs without dropping listeners or in-flight connections:
titanium reload -c twp.yaml| Trigger | OS | Notes |
|---|---|---|
titanium reload -c … | All | Preferred operator command |
| SIGHUP | Linux / macOS | Same path as reload; systemd units include ExecReload |
systemctl reload titanium | Linux (service) | Uses ExecReload=/bin/kill -HUP $MAINPID |
titanium run --watch | All | Debounced file watch on the -c path (off by default) |
Plus PUT /v1/snapshot | All | Programmatic clusters/routes; no Plus required for file reload |
Reloads live: routes (match + transforms), clusters (destinations, load-balance, affinity). Destinations left in draining or maintenance keep that state when their address, port, and TLS flag are unchanged. server: engine knobs reload too.
Needs a process restart: listeners (bind/port), certificates / ACME, TLS material, enabling or disabling Plus plugins, static files root, access-log path.
Unlike a typical YARP process recycle, Titanium keeps the process and in-flight sessions on the previous cluster snapshot until they finish.
Restart without dropping connections (Linux)
Listener, certificate and plugin changes need a new process. With server.hotRestart on (see Hot restart), titanium restart -c twp.yaml starts the new process beside the running one, waits until it is ready and owns the pid file, and reports. The old process then closes its TCP listeners, finishes what it accepted, and exits. If the new process fails to start, the old one keeps serving and the command exits 1 with the tail of <pidFile>.log. HTTP/3 and UDP listeners bind once the old process has exited.