Composable service-supervision toolkit for Rust
Malkuth helps automated, long-running programs handle supervision — graceful shutdown, health probes, coordination locks, and rolling updates:
- Pluggable transport — JSON-RPC over TCP, WebSocket, or IPC (Unix sockets /
named pipes). One
Transporttrait, dispatched by URL scheme. - Supervised workers — spawn a process, monitor its health, restart it on failure, drain connections before shutdown.
- Optional facilities — exit source, probes, heartbeat and drain hooks are traits. Use the defaults or supply your own.
- A watchdog CLI —
malkuth -- <cmd>wraps any program with file watching, a pod pool, and an L4 sticky reverse proxy.
malkuth [--watch PATH]... [--proxy PUBLIC:LO-HI] [--pod-count N] -- <cmd> [args...]
Run 5 parallel copies of your server (each listening on the PORT env var →
they self-assign 3001–3005), fronted by a sticky proxy on 3000:
malkuth --watch ./src --watch ./res \
--proxy 3000:3000-3999 --pod-count 5 \
-- cargo runThe proxy routes each client IP to a fixed backend via consistent hashing, so a client keeps hitting the same pod until that pod restarts or scales down — the basis for gray release / rolling restart. On a file change it drains and restarts one pod at a time.
Prebuilt binaries are published to npm, so you can run malkuth with a single
command — no cargo build:
npx @celestia-island/malkuth --watch ./src -- cargo run
npx @celestia-island/malkuth mcp # the MCP server (needs the mcp build)The @celestia-island/malkuth root package pulls the right platform subpackage
(-linux-x64 / -darwin-arm64 / -win32-x64) automatically. To pin a version:
npx @celestia-island/malkuth@0.1.0 -- cargo run[dependencies]
malkuth = "0.1"
# features: tcp (default) | ws | ipc | signals (default) | worker | probes |
# file-lock | lease | pg-lock | replica | leader-follower | schema | cliuse std::sync::Arc;
use malkuth::{Client, Router, Server, Supervised, Transport};
use malkuth::transport::TcpTransport;
use serde_json::json;
#[tokio::main]
async fn main() -> std::io::Result<()> {
// Bind once; build a router with the standard lifecycle RPC + your methods.
let lis = TcpTransport.listen("tcp://127.0.0.1:0").await?;
let supervised = Supervised::new().signals(); // OS-signal exit source
let ctrl = supervised.drain_controller();
let handler = Arc::new(
Router::new()
.lifecycle(ctrl, None) // Lifecycle.Drain/Status/...
.route("ping", |_| Box::pin(async { Ok(json!("pong")) })),
);
// Race the server against the exit source, then run drain hooks.
supervised.serve_rpc_listener(lis, handler).await
}Need drain triggered by your own logic instead of signals? Implement
malkuth::ExitSource and pass it via .exit(...). Want Postgres-backed
coordination? The pg-lock feature provides a CoordinationLock backend.
| Feature | Enables |
|---|---|
tcp (default) |
JSON-RPC over local/remote TCP (tokio::net) |
ws |
JSON-RPC over WebSocket (tokio-tungstenite) |
ipc |
JSON-RPC over local IPC (interprocess) |
signals (default) |
Default OS-signal ExitSource (tokio::signal) |
worker |
Supervised child-process workers (tokio::process) |
probes |
axum /healthz + /readyz router |
file-lock |
POSIX flock CoordinationLock backend (unix) |
lease |
File-lease CoordinationLock with TTL auto-expiry (crash-safe) |
pg-lock |
PostgreSQL pg_advisory_lock backend (tokio-postgres) |
replica |
In-memory InstanceRegistry |
leader-follower |
LeaseLeaderElector (over the lease backend) |
schema |
schemars::JsonSchema derives for wire types |
cli |
The malkuth watchdog binary (pod pool + sticky proxy) |
Layers 1–3 (lifecycle/drain, probes, listener handoff) and the JSON-RPC core
(codec + server/client + tcp/ws/ipc transports) are implemented and tested
end-to-end. The CLI pod pool + sticky proxy is working (e2e-verified). All three
CoordinationLock backends (file-lock, lease, pg-lock) and the
leader-follower LeaseLeaderElector are implemented. See
docs/design/ for the design.
Build malkuth with the mcp feature and run the stdio server — it exposes the
supervision toolkit to AI coding assistants over the Model Context Protocol:
malkuth mcpThe server advertises two tools: malkuth_supervise (launch a set of workers
under the supervisor with restart policies + a sliding-window rate limit;
blocks until they exit or the timeout fires, then returns the final status
snapshot) and malkuth_probe (HTTP healthz / readyz check against a service
URL). Wire it into an MCP client:
{
"mcpServers": {
"malkuth": { "command": "malkuth", "args": ["mcp"] }
}
}The mcp feature implies worker + schema; it adds rmcp and a reqwest
client for the probe tool.
SySL-1.0 (Synthetic Source License). See LICENSE or the SySL website.
For production MCP deployments, use an auto-restart wrapper to keep the server alive across updates without interrupting the client session.
#!/bin/bash while true; do /path/to/malkuth mcp sleep 0.2 done
- The wrapper runs
malkuth mcpin awhile trueloop. - If the process exits, it restarts within 0.2 seconds.
- To update:
kill $(pgrep -f "malkuth mcp" | head -1) - malkuth can also supervise other MCP tools — use it as a watcher for the entire MCP toolchain.
