GUIDE
Eight stops to a running service
Every block below is a program in this repository, copied at build time — so the guide cannot drift from the code it teaches. Run one from the root and read what it prints.
Install
One dependency, and the loader, the engine and the middlewares come with it. Every example below is a program in this repository, run from its own directory.
$ moon run src/examples/01-config
fn main {
try {
let def = @moonzero.ServiceConf::new()
println("defaults: " + describe(def))
// A partial JSON config: only the fields present override; the rest fall back
// to the same defaults `new()` uses (the `,optional` / `,default=` tags).
let json_src =
#|{ "name": "greet", "port": 9000, "log_level": "error" }
let from_json = @moonzero.ServiceConf::from_json(json_src)
println("from_json: " + describe(from_json))
// The YAML config format a service actually writes, decoded through the same
// lenient field reader so it agrees with JSON field-for-field.
let yaml_src = "name: greet\nhost: 127.0.0.1\nport: 9000\ntimeout_ms: 1500\nlog_level: debug\n"
let from_yaml = @moonzero.ServiceConf::from_yaml(yaml_src)
println("from_yaml: " + describe(from_yaml))
// LogLevel derives Compare in verbosity order, so thresholds test directly.
let debug = @moonzero.LogLevel::parse("debug")
let error = @moonzero.LogLevel::parse("error")
println(
"log levels: " +
debug.to_string() +
" < " +
error.to_string() +
" = " +
(debug < error).to_string(),
)
println(
"unknown level falls back to: " +
@moonzero.LogLevel::parse("verbose").to_string(),
)
} catch {
Refused(msg) => println("config error: " + msg)
}
// The YAML etc/*.yaml is written in, read by moonjson into the same
// Json tree the JSON loader produces. It reports its own error, with a
// position, so it is caught where it is raised.
try {
let doc = @moonyaml.loads(
"name: greet\nnested:\n port: 9000\n tags:\n - a\n - b\n",
)
// … the rest is in the file
Read one document
A service is described before it is written: a name, a host, a port, and the layers the engine should install. The loader fills what the document leaves out.
moon add moonbitstack/moonzero
moon run src/examples/01-config
Assemble the application
The engine takes your application and the layers the config asked for and hands back one ASGI application. Nothing listens yet — the host owns the socket.
moon add moonbitstack/moonzero
moon run src/examples/01-config
Wrap it in the chain
Recovery, the breaker, the shedder and the limiters are layers in the same onion; the config decides which of them exists.
moon add moonbitstack/moonzero
moon run src/examples/01-config
Let tokens through
A JWT is signed and checked under HS256, a previous secret keeps rotation seamless, and a content signature covers the body the request actually carries.
moon add moonbitstack/moonzero
moon run src/examples/01-config
Speak zRPC
Both halves of the wire run through one interceptor chain. In process is a real h2c exchange; a port is one layer away.
moon add moonbitstack/moonzero
moon run src/examples/01-config
Stop without dropping a call
A drain makes new calls come back Unavailable while the ones already running finish, and reports when the last one has.
moon add moonbitstack/moonzero
moon run src/examples/01-config
Call a peer another library wrote
The same server on a real socket, meeting a gRPC peer that shares no code with this one. The example is both ends: `serve <port>` and `call <port> <message>`.
moon add moonbitstack/moonzero
moon run src/examples/01-config