Add FSR to an existing Rust service
The FSR host is a tower::Service, so you can mount it inside a binary you already have, with your own runtime, middleware and listener.
Before you start
Install from crates.io. You need no Node and no package manager.
cargo install snapfire_compiler
cargo install snapfire_fsr_cli
fsr --version
Every command and screenshot on this page was captured with fsr 0.x.
Start a server#
use Arc;
use Host;
async
That's a complete FSR server. Host::from_cwd reads config/, the plan and the contracts. build binds every name and refuses to start if one is unanswered.
Print the report. It's the same table fsr build --verbose printed with the host's rows added.
Bind the listener yourself#
serve binds an address. serve_listener takes one you already have, which is how a test picks port zero:
use Arc;
use Host;
async
Nest it in a router you already have#
The host is a tower::Service. An existing axum router nests it under a prefix and keeps its own middleware. This needs axum in your Cargo.toml:
use Arc;
use ;
use Host;
async
async
Your API, auth layer, rate limiter and tracing middleware all still apply.
Actix works too. Turn on the host's actix feature and add actix-web = "4", then run main on actix's runtime:
use Arc;
use Host;
async
Run a request with no socket#
handle runs the whole edge for one request, cookies included, with nothing bound. You can use it for integration tests and to embed the host somewhere that isn't HTTP.
Terminate TLS when there's no proxy#
Most deployments put a proxy in front and the host never sees a certificate. For the one that doesn't, the tls feature makes the hyper listener terminate. ALPN picks HTTP/2 or HTTP/1.1 per connection.
[]
= trueReload without dropping connections#
Everything a request reads is one set of tables the host swaps whole: the plan, the contracts, the clients, the head, the static roots, the locales, the identity flow.
host.reload() rereads the configuration, the plan and the contracts and swaps the tables. This main does it on every SIGHUP, which needs tokio's signal feature:
use Arc;
use Host;
use ;
async
A host built from its configuration alone reloads by reading it again. One you gave overrides, services or an evaluator by hand needs
.reloader(...)on the builder, a function that builds it again with those added.
A request already running finishes on the tables it started with. The next one sees the new ones. Sessions aren't part of the tables, so nobody is signed out by a deploy. A reload whose [session] settings differ from the running ones is refused rather than applied.
Call it from a SIGHUP handler for config reload or from your control plane. 310 uses the same machinery to swap a mounted site while the process keeps serving.
Take names back in Rust#
The builder binds a name to whatever answers it. That can be your code. With the shop app from 500, the overrides chain between Host::from_cwd() and .build():
use Arc;
use Plan;
use ;
use Host;
async
500 covers each of these in full, including why replacing something is spelled _override and why overriding nothing is a boot error.
Next up: 520. Serve Tera templates from Rust, which renders pages from Tera templates.