RustHost is a single-binary static file server written in Rust. It serves public static content over HTTP, can add HTTPS with self-signed, manual, or ACME-managed certificates, and can expose the same site through an in-process Tor onion service powered by Arti.
RustHost is designed for small, explicit static hosting deployments where the server should be easy to run, easy to inspect, and conservative by default. It can run interactively with a terminal dashboard, or headlessly for service, CI, and remote-shell environments.
RustHost is intentionally not a web framework or application server. It does not provide users, sessions, logins, uploads, private routes, a CMS, or an admin authorization layer. If you need private or operator-only access, put RustHost behind a reverse proxy or another authenticated service.
RustHost aims to keep static hosting operationally simple:
- one binary
- one data directory
- one explicit config file
- one place to inspect runtime status
For 1.0.0, that simplicity now extends further into day-to-day operations with first-class health/readiness endpoints, a built-in Doctor workflow, richer diagnostics, and a larger operator-focused TUI.
| Area | What RustHost provides |
|---|---|
| Static hosting | Canonical-root path resolution, keep-alive HTTP/1.1, compression, range requests, SPA fallback, custom error pages |
| HTTPS | Self-signed local certs, manual PEM loading, ACME / Let's Encrypt, optional redirect listener |
| Tor | In-process Arti onion service for the same site and runtime state kept under the data directory |
| Operations | /health, /ready, structured logs, headless mode, graceful shutdown, connection limits |
| Operator UX | Main dashboard, logs view, menu index, Doctor, Diagnostics, Network, Site, Settings, Tor, and Help pages |
| Diagnostics | rusthost-cli doctor, build-aware --version, runtime diagnostics snapshots, documented release validation commands |
Static file serving
- HTTP/1.1 server using
hyperwith keep-alive support. GET,HEAD, andOPTIONShandling, with405 Method Not Allowedfor unsupported methods.- ETag and
Last-Modifiedrevalidation. - Single-range byte requests for seekable media and large files.
- Brotli and Gzip compression negotiated with
Accept-Encoding. - Precompressed sidecar support for
.brand.gzfiles. - Optional directory listings, SPA fallback routing, custom
404and503pages, and URL redirects. - Dotfiles are hidden unless explicitly enabled.
HTTPS and TLS
- HTTP-only by default.
- Self-signed localhost certificate generation for local HTTPS testing.
- Manual certificate loading from paths inside the configured data directory.
- ACME / Let's Encrypt support through
rustls-acme. - Optional HTTP-to-HTTPS redirect listener.
Tor onion service
- Built-in onion service support through Arti.
- No external Tor daemon or binary required.
- Tor state and cache are stored under the runtime data directory.
- The same static site is served over clearnet HTTP/HTTPS and onion access.
Operations and safety
- Strict TOML config deserialization and validation.
- Global and per-IP connection limits.
- Configurable graceful shutdown windows.
- Built-in
/healthand/readyendpoints for monitoring and readiness checks. - Security headers on responses, with configurable CSP presets for HTML.
- Structured access log in Combined Log Format when logging is enabled.
- Interactive dashboard with reload and log views, plus headless mode for services.
- Accurate runtime path reporting for custom
--data-dirdeployments.
Operator tooling
rusthost-cli doctorfor fast preflight checks across config, paths, listeners, TLS, Tor, favicon setup, and logging.rusthost-cli --versionoutput that includes version, build profile, short commit, and target triple.- TUI menu index with dedicated Home, Logs, Doctor, Diagnostics, Tor, Network, Site, Settings, and Help pages.
- Diagnostics snapshots that can be refreshed in the TUI for troubleshooting and support handoff.
- Release-validation commands documented in the README for repeatable release readiness checks.
Development quality
unsafeRust is forbidden by crate lints.- Strict Clippy configuration is checked by the documented quality gate.
- Project changes are intended to stay small, explicit, and reviewable.
- Integration tests exercise the real server with keep-alive, range requests, directory listings, percent-encoded paths, and mixed static assets.
RustHost is a good fit for:
- previewing a local static site from a single binary
- hosting a small public static site
- serving lightweight internal documentation
- publishing static content with optional onion access
- testing HTTPS behavior without adding a separate web server
RustHost is not intended for:
- dynamic application hosting
- authenticated user workflows
- file uploads
- operator dashboards exposed directly to the public internet
- replacing a full reverse proxy when you need complex ingress policy
RustHost requires Rust 1.90 or newer.
cargo build --releaseThe release binary is written to:
./target/release/rusthost-cliQuick checks after building:
./target/release/rusthost-cli --version
./target/release/rusthost-cli --help
./target/release/rusthost-cli doctor --data-dir ./tmp-rusthost-checkUse --serve when you want to serve a directory directly without creating a persistent settings.toml.
./target/release/rusthost-cli --serve ./publicUseful variants:
./target/release/rusthost-cli --serve ./public --port 3000
./target/release/rusthost-cli --serve ./public --no-tor
./target/release/rusthost-cli --serve ./public --headlessIn one-shot mode, RustHost binds to 127.0.0.1, enables directory listings, disables file logging, and uses the default static-file behavior. Tor is enabled unless --no-tor is supplied.
Use managed mode when you want persistent configuration, logs, certificates, and Tor state.
./target/release/rusthost-cli --data-dir ./rusthost-dataOn first run, RustHost creates:
rusthost-data/
settings.toml
site/
runtime/
Put your static files in rusthost-data/site/. In interactive mode, press R in the dashboard to reload site state after changing files. On Unix, sending SIGHUP also triggers the reload path.
After startup, RustHost supports a compact operator validation flow:
curl -i http://127.0.0.1:8080/health
curl -i http://127.0.0.1:8080/ready
./target/release/rusthost-cli doctor --data-dir ./rusthost-data/health returns a simple liveness response. /ready returns 200 READY only after startup has completed and the active data, site, and log directories are usable.
The generated settings.toml is the main control surface. The most important defaults are:
- HTTP listens on
127.0.0.1:8080. - HTTPS is disabled.
- Tor is enabled in generated configs.
- The interactive dashboard is enabled.
- The site directory is
site, relative to the data directory. /favicon.icoservesrusthost-data/site/favicon.icoby default.- Logging writes under
runtime/logs/when enabled.
Common sections:
| Section | Purpose |
|---|---|
[server] |
bind address, port, connection limits, CSP preset, trusted proxies, browser opening |
[site] |
site directory, index file, favicon, directory listing, dotfile exposure, SPA fallback, custom error pages |
[tls] |
HTTPS listener, redirect behavior, ACME, manual certificates |
[tor] |
onion service enablement and Tor shutdown grace period |
[logging] |
application log level, log file, dependency log filtering |
[console] |
interactive dashboard behavior |
[identity] |
dashboard instance name |
[[redirects]] |
exact-path HTTP redirects evaluated before filesystem resolution |
Minimal local HTTP configuration:
[server]
port = 8080
bind = "127.0.0.1"
[site]
directory = "site"
index_file = "index.html"Network exposure is explicit. Use bind = "0.0.0.0" only when you intend RustHost to listen on all IPv4 interfaces.
[server]
bind = "0.0.0.0"
port = 8080
max_connections = 256
max_connections_per_ip = 16SPA routing and custom error pages:
[site]
spa_routing = true
error_404 = "404.html"
error_503 = "503.html"Redirects:
[[redirects]]
from = "/old-page"
to = "/new-page"
status = 301Redirect status must be 301 or 302. Redirect targets must be safe to emit in an HTTP Location header.
When [console] interactive = true, RustHost starts in the main dashboard and exposes a wider operator console than earlier releases.
- Local HTTP/HTTPS/onion endpoints
- uptime, request counts, and unique visitor counts
- current site directory under the active data directory
- transient status messages such as reload completion
Hshows the global help screenRrescans the site directory and refreshes serving stateOopens the local URL in the system browserLopens the recent log viewMopens the menu indexQopens shutdown confirmation from the dashboard or top-level menu
Homereturns to the main dashboardLogsopens the recent log viewDoctorsummarizes readiness checks and can run bounded deep checksDiagnosticsbuilds a compact troubleshooting snapshot and can refresh or clear status messagesTorshows onion-service state and related controlsNetworkshows configured listeners and local listener checksSitesummarizes the served root and key site filesSettingsshows effective runtime settings and embedded diagnostics text when copy fallback is neededHelpprovides in-console guidance for controls, CLI commands, networking, Tor, and troubleshooting
Nested-page navigation is intentionally conservative: page-local controls stay on the current page, Esc backs out safely, and Q does not tear down RustHost from every nested screen.
RustHost resolves every requested path against the canonical site root and rejects escapes outside that root. Direct dotfile requests and dotfile entries in directory listings are blocked unless site.expose_dotfiles = true.
Directory requests are normalized with trailing slash redirects. If a directory contains the configured index_file, that file is served. If it does not and directory listings are enabled, RustHost returns a generated listing capped at 512 entries. Otherwise, it returns the fallback or not-found response.
For cache and transfer behavior:
- HTML responses use
Cache-Control: no-cache. - Filenames with an 8 to 16 character hexadecimal hash segment are treated as immutable assets.
- Other assets default to
Cache-Control: no-cache. - Compressible responses over 1024 bytes can be streamed with Brotli or Gzip.
- Existing
.brand.gzsidecars are preferred when they match the requested representation. - Range requests are served only for identity responses.
Security-related response headers include:
X-Content-Type-Options: nosniffX-Frame-Options: SAMEORIGINReferrer-Policy: no-referrerPermissions-Policy: camera=(), microphone=(), geolocation=()Strict-Transport-Securityon HTTPS responsesContent-Security-Policyfor HTML when[server] csp_levelis notoff
When HTTPS and Tor are both active, RustHost can add an Onion-Location header on clearnet HTTPS responses after the onion address is available.
RustHost now exposes a small set of built-in operational surfaces that are useful in local testing, service management, and release validation.
- accepts
GETandHEAD - returns
200 OK - is routed before static files
- can be used as a simple liveness probe
- accepts
GETandHEAD - returns
200 READYonly when startup is complete - returns
503 NOT READY: ...with a short reason when required runtime state is missing or unusable - checks the active data directory, site root, and log directory instead of assuming defaults
Use Doctor when you want preflight validation without starting the full interactive console:
rusthost-cli doctor --data-dir ./rusthost-dataDoctor checks:
- config parsing and validation
- filesystem and site-root expectations
- bind/listener sanity
- TLS and Tor configuration
- favicon and logging setup
- bounded local deep checks when run through the TUI
Doctor also writes doctor.log under the active runtime log directory so the report can be retained with other operator diagnostics.
Enable HTTPS with:
[tls]
enabled = true
port = 8443If no manual certificate or ACME config is enabled, RustHost generates or reuses a self-signed development certificate under:
runtime/tls/dev/
The generated certificate covers:
localhost127.0.0.1::1
This mode is for local development and testing.
[tls]
enabled = true
port = 443
[tls.manual_cert]
cert_path = "runtime/tls/manual/fullchain.pem"
key_path = "runtime/tls/manual/privkey.pem"Manual certificate paths are relative to the data directory. Absolute paths, parent directory traversal, and symlink escapes outside the data directory are rejected.
[tls]
enabled = true
port = 443
redirect_http = true
http_port = 80
[tls.acme]
enabled = true
domains = ["example.com", "www.example.com"]
email = "ops@example.com"
staging = true
cache_dir = "runtime/tls/acme"Start with staging = true, verify DNS and public reachability, then switch to staging = false only after the staging flow works. RustHost validates ACME domains before startup: domains must be lowercase ASCII fully qualified domain names, not IP addresses, bare hostnames, duplicates, or wildcards.
ACME uses TLS-ALPN-01 through the HTTPS listener. The cache directory is relative to the data directory.
When [tor] enabled = true, RustHost starts Arti in-process and publishes an onion service for the same static site.
[tor]
enabled = true
shutdown_grace_secs = 5What to expect:
- The first bootstrap needs outbound network access.
- Arti downloads Tor directory data on first run.
- The full onion address appears in the dashboard and logs once available.
- Tor private state and cache live under
runtime/tor/. - Preserve the Tor state directory if onion address continuity matters.
For public or sensitive deployments, run RustHost under a dedicated OS account and keep the data directory private.
- Keep
bind = "127.0.0.1"for local-only use. - Use
bind = "0.0.0.0"or a public interface only when the host firewall and deployment model are intentional. - Use
--headlessor[console] interactive = falsefor services, containers, CI, and remote shells. - Put authentication, private routes, IP allowlists, and advanced rate limiting in a reverse proxy or separate service.
- Only set
[server] trusted_proxiesto proxy IPs you control. Otherwise, RustHost ignoresX-Forwarded-Forand uses the TCP peer address. - Back up certificate state and Tor state if continuity matters.
- Review the site directory before exposing it. RustHost serves static files; it does not know which files are sensitive.
rusthost-cli [OPTIONS]
OPTIONS:
--config <path> Override the path to settings.toml
--data-dir <path> Override the data-directory root
--serve <dir> Serve a directory directly, with no first-run setup
--port <n> Port for --serve mode, default 8080
--no-tor Disable Tor in --serve mode
--headless Disable the interactive console
doctor Run fast Doctor preflight checks and exit
-V, --version Print version and exit
-h, --help Print help and exit
Both --flag value and --flag=value forms are accepted.
--version prints:
- RustHost version
- build profile
- short commit
- target triple
Run the binary from source:
cargo run -- --help
cargo run -- --serve ./publicUse this command set before a release candidate or readiness handoff.
Initialize ./tmp-release-check once with an isolated live run, then run the validation commands below against that same temp data directory:
cargo fmt --all --check
cargo clippy --workspace --all-targets --all-features -- -D warnings
cargo test --workspace --all-features
cargo build --bin rusthost-cli
target/debug/rusthost-cli --version
target/debug/rusthost-cli doctor --data-dir ./tmp-release-checkFor live /health and /ready verification, start RustHost against an isolated temporary data directory such as ./tmp-release-check rather than a real deployment data directory.
Suggested manual release checks:
curl -i http://127.0.0.1:8080/health
curl -i http://127.0.0.1:8080/readyAdditional quality gates:
cargo clippy --all-targets --all-features -- -D warnings
cargo test --all-targetsThe test suite includes unit tests for config, TLS, path handling, and Tor helpers, plus integration tests that start the real HTTP server and inspect raw HTTP responses.
src/main.rs CLI entry point
src/runtime/ startup, reload, shutdown, shared state
src/config/ TOML schema, defaults, validation
src/server/ HTTP, HTTPS, redirects, static file handling
src/tls/ self-signed, manual, and ACME certificate support
src/tor/ Arti onion service integration
src/console/ terminal dashboard and input handling
src/logging/ application and access logging
tests/ integration and stress tests
docs/ architecture diagrams and design notes
- Setup guide
- Change history
- Contributing guide
- Module architecture diagram
- Directory structure diagram
- Lifecycle diagram
RustHost is licensed under the MIT License.