core: sausage-vfs per process (/, /proc, /self, /run), activation, graph, watcher, upgrades

- /proc is a core layer: core/ (PROTOCOL.md, graph.json, graph.dot,
  locks.json, plugins/<name>/discover.json and client.rs) and one folder
  per process (info.json, log, and its /self files).
- Locks are released only after the task ended, the store dropped, and
  in-flight file operations returned.
- core.activate and core.revert with copies, discovery, compatibility
  checks of imports and consumers, groups, force. An admission gate
  makes the snapshot of old processes complete.
- Graph of discover documents by hash, refreshed by a file watcher.
- Upgrades: grace periods (x-sausage-grace), stop, service restarts,
  rpc.retired.
- SDK plugins in the real core (cli/tests/sdk.rs).
This commit is contained in:
2026-10-23 01:05:19 +01:00
parent 151550242b
commit edc2d1a63b
21 changed files with 2306 additions and 101 deletions
+34 -19
View File
@@ -1,31 +1,46 @@
//! Services: the calls in `/config/boot.json` (PROTOCOL.md section 8). The
//! core starts them in order at boot.
//! Ordered boot services and their identities for upgrade restarts (PROTOCOL.md section 8).
use crate::Host;
use serde_json::{Value, json};
use uuid::Uuid;
#[derive(Clone)]
pub(crate) struct Service {
pub(crate) method: String,
pub(crate) params: Value,
pub(crate) request: Value,
}
impl Host {
/// Start each service of `/config/boot.json` as a detached call with the
/// parent `core`, in order. A failure goes to the journal, and boot
/// continues.
/// Starts discovery in the background, then waits for each boot service in order.
pub(crate) async fn boot(&self) -> anyhow::Result<()> {
self.watch_plugins()?;
let boot: Vec<Value> = serde_json::from_slice(
&tokio::fs::read(self.0.world.root.join("config/boot.json")).await?,
)?;
for service in boot {
let result = self
.route(
"core".into(),
Some(json!(Uuid::now_v7().to_string())),
service["method"].as_str().unwrap_or("").into(),
service["params"].clone(),
true,
)
.await;
if let Err(e) = result {
self.0.journal.write("core", "core", "core", &e.to_string());
}
self.start_service(
service["method"].as_str().unwrap_or("").into(),
service["params"].clone(),
)
.await;
}
Ok(())
}
/// Records a unique request identity before the service can answer or exit.
pub(crate) async fn start_service(&self, method: String, params: Value) {
let request = json!(uuid::Uuid::now_v7().to_string());
self.0.state.lock().unwrap().services.push(Service {
method: method.clone(),
params: params.clone(),
request: request.clone(),
});
self.0
.journal
.write("core", "core", "core", &format!("service start {method}"));
if let Err(error) = self
.route("core".into(), Some(request), method, params, true)
.await
{
self.0
.journal
.write("core", "core", "core", &format!("service failed {error}"));
}
}
}