From 1d291e079b67c66a214d2db56e3c6b0b56a1e613 Mon Sep 17 00:00:00 2001 From: mfw78 Date: Fri, 17 Jul 2026 00:28:13 +0000 Subject: [PATCH] feat: extract a generic launcher and split the bare and cow binaries nexum-launch owns the shared CLI, config load, tracing init, and the preset-driven run; nexum-cli becomes the bare Ext=() engine binary over CoreRuntime with no cow edge; the cow composition root moves to a new shepherd binary wiring the cow-api extension as a Runtime preset. The venue-agnostic guard's crate-graph check now covers the launcher and the bare binary, and the cow deployment tooling builds shepherd. --- Cargo.lock | 23 +++++++- Cargo.toml | 2 + Dockerfile | 12 ++-- README.md | 4 +- crates/nexum-cli/Cargo.toml | 7 +-- crates/nexum-cli/src/cli.rs | 60 -------------------- crates/nexum-cli/src/launch.rs | 58 -------------------- crates/nexum-cli/src/main.rs | 47 ++-------------- crates/nexum-launch/Cargo.toml | 17 ++++++ crates/nexum-launch/src/cli.rs | 85 +++++++++++++++++++++++++++++ crates/nexum-launch/src/lib.rs | 66 ++++++++++++++++++++++ crates/nexum-runtime/src/builder.rs | 2 +- crates/shepherd/Cargo.toml | 21 +++++++ crates/shepherd/src/main.rs | 60 ++++++++++++++++++++ docker-compose.soak.yml | 2 +- justfile | 13 +++-- scripts/check-venue-agnostic.sh | 34 +++++++----- scripts/e2e-run.sh | 6 +- scripts/load-run.sh | 6 +- 19 files changed, 321 insertions(+), 204 deletions(-) delete mode 100644 crates/nexum-cli/src/cli.rs delete mode 100644 crates/nexum-cli/src/launch.rs create mode 100644 crates/nexum-launch/Cargo.toml create mode 100644 crates/nexum-launch/src/cli.rs create mode 100644 crates/nexum-launch/src/lib.rs create mode 100644 crates/shepherd/Cargo.toml create mode 100644 crates/shepherd/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index 217777ba..bf3873ae 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3575,10 +3575,18 @@ name = "nexum-cli" version = "0.2.0" dependencies = [ "anyhow", - "clap", + "nexum-launch", "nexum-runtime", - "shepherd-cow-host", "tokio", +] + +[[package]] +name = "nexum-launch" +version = "0.2.0" +dependencies = [ + "anyhow", + "clap", + "nexum-runtime", "tracing", "tracing-subscriber", ] @@ -5158,6 +5166,17 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "shepherd" +version = "0.2.0" +dependencies = [ + "anyhow", + "nexum-launch", + "nexum-runtime", + "shepherd-cow-host", + "tokio", +] + [[package]] name = "shepherd-backtest" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 418564d7..0a562877 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,7 @@ members = [ "crates/cow-venue", "crates/nexum-cli", + "crates/nexum-launch", "crates/nexum-macros", "crates/nexum-runtime", "crates/nexum-sdk", @@ -11,6 +12,7 @@ members = [ "crates/nexum-venue-sdk", "crates/nexum-venue-test", "crates/nexum-world", + "crates/shepherd", "crates/shepherd-backtest", "crates/shepherd-cow-host", "crates/shepherd-sdk", diff --git a/Dockerfile b/Dockerfile index eb7fa3f0..d7465ff5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ # syntax=docker/dockerfile:1.6 # -# Multi-stage build for `nexum` (Shepherd) - the engine binary -# plus the five production WASM modules baked into a single image. +# Multi-stage build for `shepherd` - the cow composition-root engine +# binary plus the five production WASM modules baked into a single image. # # Stage 1 (`build`): full Rust toolchain + wasm32-wasip2 target, builds # the engine in release mode + each module to a Component Model wasm @@ -65,7 +65,7 @@ FROM chef AS build # changed workspace crate. `--locked` stays on the real builds below, which # validate the committed Cargo.lock verbatim. COPY --from=planner /src/recipe.json recipe.json -RUN cargo chef cook --release -p nexum-cli --recipe-path recipe.json \ +RUN cargo chef cook --release -p shepherd --recipe-path recipe.json \ && cargo chef cook --release --target wasm32-wasip2 \ -p twap-monitor -p ethflow-watcher -p price-alert \ -p balance-tracker -p stop-loss --recipe-path recipe.json @@ -77,7 +77,7 @@ COPY . . # Engine binary in release. --locked ensures the committed Cargo.lock # is used verbatim so builds are reproducible. -RUN cargo build -p nexum-cli --release --locked +RUN cargo build -p shepherd --release --locked # Five production modules. The wasm artefacts land under # `target/wasm32-wasip2/release/.wasm`. @@ -108,7 +108,7 @@ RUN apt-get update \ && install -d -o root -g root -m 0755 /etc/shepherd # Engine binary. -COPY --from=build /src/target/release/nexum /usr/local/bin/nexum +COPY --from=build /src/target/release/shepherd /usr/local/bin/shepherd # Module .wasm artefacts. The Component Model wasm files are loaded # by the engine at boot via the `[[modules]]` entries in engine.toml. @@ -138,5 +138,5 @@ EXPOSE 9100 # `--engine-config /etc/shepherd/engine.toml` matches the production # guide's expected mount point. Operators override via # `docker run ... -v /path/to/engine.toml:/etc/shepherd/engine.toml:ro`. -ENTRYPOINT ["/usr/bin/tini", "--", "nexum"] +ENTRYPOINT ["/usr/bin/tini", "--", "shepherd"] CMD ["--engine-config", "/etc/shepherd/engine.toml"] diff --git a/README.md b/README.md index 91026dd0..8c94e3ac 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,9 @@ Looking for the org? See **[github.com/nullislabs](https://github.com/nullislabs | Path | Purpose | | --- | --- | | `crates/nexum-runtime/` | The **engine** - the Nexum Runtime's reference host: a wasmtime implementation of the `nexum:host` contract. | -| `crates/nexum-cli/` | The `nexum` binary - a thin CLI over the runtime library. | +| `crates/nexum-launch/` | The generic launcher library - shared CLI, config load, tracing, and the preset-driven launch. | +| `crates/nexum-cli/` | The bare `nexum` binary - the core lattice with no extension payload. | +| `crates/shepherd/` | The `shepherd` binary - the cow composition root wiring the cow-api extension. | | `crates/nexum-sdk/` | Generic guest SDK - the host trait seam, bind macro, chain/config/address helpers, wasi:http `fetch`, and tracing facade for any module. | | `crates/shepherd-sdk/` | CoW-domain guest SDK - the cow-api trait and CoW Protocol helpers on top of `nexum-sdk`. | | `wit/nexum-host/` | The **`nexum:host`** WIT package - the host/guest contract every engine implements and every module imports. | diff --git a/crates/nexum-cli/Cargo.toml b/crates/nexum-cli/Cargo.toml index cc727db9..05bab22d 100644 --- a/crates/nexum-cli/Cargo.toml +++ b/crates/nexum-cli/Cargo.toml @@ -13,13 +13,8 @@ name = "nexum" path = "src/main.rs" [dependencies] +nexum-launch = { path = "../nexum-launch" } nexum-runtime = { path = "../nexum-runtime" } -# Composition root wires the cow-api host extension into the reference -# lattice; the runtime itself carries no cow dependency. -shepherd-cow-host = { path = "../shepherd-cow-host" } anyhow.workspace = true -clap.workspace = true tokio.workspace = true -tracing.workspace = true -tracing-subscriber.workspace = true diff --git a/crates/nexum-cli/src/cli.rs b/crates/nexum-cli/src/cli.rs deleted file mode 100644 index 82b7739f..00000000 --- a/crates/nexum-cli/src/cli.rs +++ /dev/null @@ -1,60 +0,0 @@ -//! CLI surface for the `nexum` binary, derived via clap. -//! -//! The 0.2 binary accepts either a positional ` []` -//! shortcut that synthesises a one-module engine config, or a -//! `--engine-config ` flag that points at a TOML declaring -//! multiple modules. Production deployments use the second form; the -//! positional shortcut stays for parity with the M1 reference CLI and -//! for smoke tests. - -use std::path::PathBuf; - -use clap::Parser; - -/// Parsed CLI surface. -/// -/// `nexum [ []] [--engine-config ] [--pretty-logs]` -/// -/// Positional `` is a backwards-compat shortcut that -/// synthesises a one-module engine config. Production deployments pass -/// `--engine-config` and declare modules in TOML. -/// -/// `--pretty-logs` selects the human-readable tracing formatter (the -/// historical 0.1 default). Without the flag the engine emits JSON -/// log lines per the structured-logging contract: a single -/// `jq` / Loki / Grafana stream reconstructs the full timeline of -/// any dispatch, host call, or order submission. -#[derive(Parser, Debug, Default)] -#[command( - name = "nexum", - about = "Run one or more Wasm Component modules under the Shepherd supervisor", - long_about = None, - version, -)] -pub struct Cli { - /// Optional positional path to a Wasm Component file. Synthesises - /// a one-module engine config when no `--engine-config` is given. - pub wasm: Option, - - /// Optional positional path to the module's `module.toml` manifest. - /// Only consulted alongside the positional `wasm` shortcut. - pub manifest: Option, - - /// Optional explicit path to the engine-wide `engine.toml` config. - /// When omitted, the engine resolves the default search path - /// documented in `engine_config::load_or_default`. - #[arg(long = "engine-config")] - pub engine_config: Option, - - /// Use the human-readable tracing formatter instead of the - /// default JSON formatter (structured-logging contract). - #[arg(long = "pretty-logs")] - pub pretty_logs: bool, - - /// Override the chain-log poller's per-block `eth_getLogs` - /// concurrency during backfill. Higher catches up faster at more - /// node load. Overrides `[engine] log_backfill_concurrency` when - /// set. - #[arg(long = "log-backfill-concurrency")] - pub log_backfill_concurrency: Option, -} diff --git a/crates/nexum-cli/src/launch.rs b/crates/nexum-cli/src/launch.rs deleted file mode 100644 index dd67890c..00000000 --- a/crates/nexum-cli/src/launch.rs +++ /dev/null @@ -1,58 +0,0 @@ -//! Composition root: bind the reference lattice (core backends plus the -//! cow-api extension in the `Ext` slot), build the shared backends and the -//! extension list, then hand off to the generic runtime launch. - -use std::path::Path; - -use nexum_runtime::addons::{PrometheusAddOn, RuntimeAddOn}; -use nexum_runtime::builder::RuntimeBuilder; -use nexum_runtime::engine_config::EngineConfig; -use nexum_runtime::host::component::{ - ComponentsBuilder, LocalStoreBuilder, ProviderPoolBuilder, RuntimeTypes, -}; -use nexum_runtime::host::local_store_redb::LocalStore; -use nexum_runtime::host::provider_pool::ProviderPool; -use shepherd_cow_host::{ReferenceExt, ReferenceExtBuilder, extension}; - -/// The backends the reference engine ships: the core seams plus the -/// cow-api extension payload in the [`Ext`](RuntimeTypes::Ext) slot. -#[derive(Debug, Clone, Copy, Default)] -struct ReferenceTypes; - -impl RuntimeTypes for ReferenceTypes { - type Chain = ProviderPool; - type Store = LocalStore; - type Ext = ReferenceExt; -} - -/// Build the reference backends and extension list, then run until shutdown. -pub async fn run_from_config( - engine_cfg: &EngineConfig, - wasm: Option<&Path>, - manifest: Option<&Path>, -) -> anyhow::Result<()> { - // Attach the reference add-on set. The binary ships the Prometheus - // exporter; an embedder omits or replaces it by choosing a different - // list here. - let add_ons: [&dyn RuntimeAddOn; 1] = [&PrometheusAddOn]; - - // Assemble and launch over the type-state builder: bind the reference - // lattice, wire cow-api as an extension (linker hook plus capability - // namespace; the core runtime knows nothing of cow, it plugs in here), - // name the component builders, then drive to a running handle and block - // until the event loop returns on shutdown. - RuntimeBuilder::new(engine_cfg) - .with_types::() - .with_extensions([extension::()]) - .with_module_source(wasm.map(Path::to_path_buf), manifest.map(Path::to_path_buf)) - .with_components(ComponentsBuilder::new( - ProviderPoolBuilder, - LocalStoreBuilder, - ReferenceExtBuilder, - )) - .with_add_ons(&add_ons) - .launch() - .await? - .wait() - .await -} diff --git a/crates/nexum-cli/src/main.rs b/crates/nexum-cli/src/main.rs index f2ce5f46..c7be67c3 100644 --- a/crates/nexum-cli/src/main.rs +++ b/crates/nexum-cli/src/main.rs @@ -1,48 +1,11 @@ -#![cfg_attr(not(test), warn(unused_crate_dependencies))] - -mod cli; -mod launch; +//! The bare `nexum` engine binary: the core lattice with no extension +//! payload, composed over the generic launcher. -use clap::Parser; -use tracing::info; -use tracing_subscriber::EnvFilter; +#![cfg_attr(not(test), warn(unused_crate_dependencies))] -use crate::cli::Cli; -use nexum_runtime::engine_config; +use nexum_runtime::preset::CoreRuntime; #[tokio::main] async fn main() -> anyhow::Result<()> { - let cli = Cli::parse(); - - let mut engine_cfg = engine_config::load_or_default(cli.engine_config.as_deref())?; - if let Some(n) = cli.log_backfill_concurrency { - engine_cfg.engine.log_backfill_concurrency = n; - } - - let env_filter = EnvFilter::try_from_default_env() - .or_else(|_| EnvFilter::try_new(&engine_cfg.engine.log_level)) - .unwrap_or_else(|_| EnvFilter::new("info")); - // Structured logging: JSON by default (machine-readable - // for production; one `jq` query reconstructs any dispatch - // timeline); `--pretty-logs` opts back into the 0.1 human-readable - // formatter for local dev. The same `EnvFilter` applies to both - // so `RUST_LOG=debug` works identically. - if cli.pretty_logs { - tracing_subscriber::fmt() - .with_env_filter(env_filter) - .with_target(true) - .init(); - } else { - tracing_subscriber::fmt() - .with_env_filter(env_filter) - .with_target(true) - .json() - .flatten_event(true) - .with_current_span(false) - .init(); - } - - info!("nexum starting"); - - launch::run_from_config(&engine_cfg, cli.wasm.as_deref(), cli.manifest.as_deref()).await + nexum_launch::run("nexum", CoreRuntime).await } diff --git a/crates/nexum-launch/Cargo.toml b/crates/nexum-launch/Cargo.toml new file mode 100644 index 00000000..aff18adc --- /dev/null +++ b/crates/nexum-launch/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "nexum-launch" +version = "0.2.0" +edition.workspace = true +license.workspace = true +repository.workspace = true + +[lints] +workspace = true + +[dependencies] +nexum-runtime = { path = "../nexum-runtime" } + +anyhow.workspace = true +clap.workspace = true +tracing.workspace = true +tracing-subscriber.workspace = true diff --git a/crates/nexum-launch/src/cli.rs b/crates/nexum-launch/src/cli.rs new file mode 100644 index 00000000..b9af9baa --- /dev/null +++ b/crates/nexum-launch/src/cli.rs @@ -0,0 +1,85 @@ +//! Shared CLI surface for engine binaries, derived via clap. + +use std::path::PathBuf; + +use clap::{CommandFactory, FromArgMatches, Parser}; + +/// Parsed CLI surface. +/// +/// ` [ []] [--engine-config ] [--pretty-logs]` +/// +/// Positional `` synthesises a one-module engine config. +/// Production deployments pass `--engine-config` and declare modules in +/// TOML. +/// +/// `--pretty-logs` selects the human-readable tracing formatter; without +/// it the engine emits JSON log lines per the structured-logging contract. +#[derive(Parser, Debug, Default)] +#[command( + about = "Run one or more Wasm Component modules under the engine supervisor", + long_about = None, + version, +)] +pub struct Cli { + /// Optional positional path to a Wasm Component file. Synthesises + /// a one-module engine config when no `--engine-config` is given. + pub wasm: Option, + + /// Optional positional path to the module's `module.toml` manifest. + /// Only consulted alongside the positional `wasm` shortcut. + pub manifest: Option, + + /// Optional explicit path to the engine-wide `engine.toml` config. + /// When omitted, the engine resolves the default search path + /// documented in `engine_config::load_or_default`. + #[arg(long = "engine-config")] + pub engine_config: Option, + + /// Use the human-readable tracing formatter instead of the + /// default JSON formatter (structured-logging contract). + #[arg(long = "pretty-logs")] + pub pretty_logs: bool, + + /// Override the chain-log poller's per-block `eth_getLogs` + /// concurrency during backfill. Higher catches up faster at more + /// node load. Overrides `[engine] log_backfill_concurrency` when + /// set. + #[arg(long = "log-backfill-concurrency")] + pub log_backfill_concurrency: Option, +} + +impl Cli { + /// Parse the process arguments under the binary's `name`, exiting on + /// `--help`/`--version` or a usage error. + #[must_use] + pub fn parse_as(name: &'static str) -> Self { + let matches = Self::command().name(name).get_matches(); + Self::from_arg_matches(&matches).unwrap_or_else(|err| err.exit()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + /// The flags land on the parsed surface under a caller-supplied name. + #[test] + fn flags_parse_under_a_supplied_name() { + let matches = Cli::command() + .name("nexum") + .try_get_matches_from([ + "nexum", + "--engine-config", + "engine.toml", + "--pretty-logs", + "--log-backfill-concurrency", + "8", + ]) + .expect("valid arguments parse"); + let cli = Cli::from_arg_matches(&matches).expect("matches destructure"); + assert_eq!(cli.engine_config, Some(PathBuf::from("engine.toml"))); + assert!(cli.pretty_logs); + assert_eq!(cli.log_backfill_concurrency, Some(8)); + assert!(cli.wasm.is_none()); + } +} diff --git a/crates/nexum-launch/src/lib.rs b/crates/nexum-launch/src/lib.rs new file mode 100644 index 00000000..6994ca29 --- /dev/null +++ b/crates/nexum-launch/src/lib.rs @@ -0,0 +1,66 @@ +//! Generic engine launcher: parse the shared CLI, load the engine config, +//! initialise tracing, and drive a [`Runtime`] preset until shutdown. +//! +//! A binary is one line: `nexum_launch::run("nexum", CoreRuntime)`. The +//! preset supplies the lattice, backends, extension list, and add-ons; +//! this crate knows nothing beyond the runtime seam. + +#![cfg_attr(not(test), warn(unused_crate_dependencies))] + +mod cli; + +pub use cli::Cli; + +use nexum_runtime::builder::RuntimeBuilder; +use nexum_runtime::engine_config::{self, EngineConfig}; +use nexum_runtime::preset::Runtime; +use tracing::info; +use tracing_subscriber::EnvFilter; + +/// Parse the process arguments as `name`, then [`launch`] the preset. +pub async fn run(name: &'static str, preset: R) -> anyhow::Result<()> { + launch(name, preset, Cli::parse_as(name)).await +} + +/// Load the config, initialise tracing, and run the preset until shutdown. +pub async fn launch(name: &str, preset: R, cli: Cli) -> anyhow::Result<()> { + let mut engine_cfg = engine_config::load_or_default(cli.engine_config.as_deref())?; + if let Some(n) = cli.log_backfill_concurrency { + engine_cfg.engine.log_backfill_concurrency = n; + } + + init_tracing(cli.pretty_logs, &engine_cfg); + + info!("{name} starting"); + + RuntimeBuilder::new(&engine_cfg) + .with_runtime(preset) + .with_module_source(cli.wasm, cli.manifest) + .launch() + .await? + .wait() + .await +} + +/// Install the global tracing subscriber: JSON by default (machine-readable +/// for production), the human-readable formatter behind `--pretty-logs`. +/// The same [`EnvFilter`] applies to both, so `RUST_LOG` works identically. +fn init_tracing(pretty: bool, engine_cfg: &EngineConfig) { + let env_filter = EnvFilter::try_from_default_env() + .or_else(|_| EnvFilter::try_new(&engine_cfg.engine.log_level)) + .unwrap_or_else(|_| EnvFilter::new("info")); + if pretty { + tracing_subscriber::fmt() + .with_env_filter(env_filter) + .with_target(true) + .init(); + } else { + tracing_subscriber::fmt() + .with_env_filter(env_filter) + .with_target(true) + .json() + .flatten_event(true) + .with_current_span(false) + .init(); + } +} diff --git a/crates/nexum-runtime/src/builder.rs b/crates/nexum-runtime/src/builder.rs index 49eb53aa..7c836f07 100644 --- a/crates/nexum-runtime/src/builder.rs +++ b/crates/nexum-runtime/src/builder.rs @@ -9,7 +9,7 @@ //! loop - and returns a [`RuntimeHandle`] owning the manager and the //! running tasks. //! -//! The reference binary reaches this through its `run_from_config` one-liner; +//! The engine binaries reach this through the `nexum-launch` preset run; //! an embedder holding pre-built backends constructs an [`AssembledRuntime`] //! and calls [`LaunchRuntime::launch`] directly. For the common case, //! [`RuntimeBuilder::runtime`] binds a [`Runtime`] preset that bundles the diff --git a/crates/shepherd/Cargo.toml b/crates/shepherd/Cargo.toml new file mode 100644 index 00000000..63e99085 --- /dev/null +++ b/crates/shepherd/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "shepherd" +version = "0.2.0" +edition.workspace = true +license.workspace = true +repository.workspace = true + +[lints] +workspace = true + +[[bin]] +name = "shepherd" +path = "src/main.rs" + +[dependencies] +nexum-launch = { path = "../nexum-launch" } +nexum-runtime = { path = "../nexum-runtime" } +shepherd-cow-host = { path = "../shepherd-cow-host" } + +anyhow.workspace = true +tokio.workspace = true diff --git a/crates/shepherd/src/main.rs b/crates/shepherd/src/main.rs new file mode 100644 index 00000000..4022ff57 --- /dev/null +++ b/crates/shepherd/src/main.rs @@ -0,0 +1,60 @@ +//! The `shepherd` binary: the cow composition root. Binds the reference +//! lattice with the cow-api extension payload in the `Ext` slot and hands +//! it to the generic launcher; the engine itself stays cow-free. + +#![cfg_attr(not(test), warn(unused_crate_dependencies))] + +use std::sync::Arc; + +use nexum_runtime::addons::{AddOns, PrometheusAddOn}; +use nexum_runtime::host::component::{ + ComponentsBuilder, LocalStoreBuilder, LogPipelineBuilder, ProviderPoolBuilder, RuntimeTypes, +}; +use nexum_runtime::host::extension::Extension; +use nexum_runtime::host::local_store_redb::LocalStore; +use nexum_runtime::host::provider_pool::ProviderPool; +use nexum_runtime::preset::Runtime; +use shepherd_cow_host::{ReferenceExt, ReferenceExtBuilder, extension}; + +/// The reference lattice: the core backends with the cow-api payload in +/// the `Ext` slot. +#[derive(Debug, Clone, Copy, Default)] +struct ReferenceTypes; + +impl RuntimeTypes for ReferenceTypes { + type Chain = ProviderPool; + type Store = LocalStore; + type Ext = ReferenceExt; +} + +/// The cow preset: reference backends, the cow-api extension, and the +/// Prometheus add-on. +#[derive(Debug, Clone, Copy, Default)] +struct ShepherdRuntime; + +impl Runtime for ShepherdRuntime { + type Types = ReferenceTypes; + type ChainBuilder = ProviderPoolBuilder; + type StoreBuilder = LocalStoreBuilder; + type ExtBuilder = ReferenceExtBuilder; + type LogsBuilder = LogPipelineBuilder; + + fn components( + self, + ) -> ComponentsBuilder { + ComponentsBuilder::new(ProviderPoolBuilder, LocalStoreBuilder, ReferenceExtBuilder) + } + + fn add_ons(&self) -> AddOns { + vec![Box::new(PrometheusAddOn)] + } + + fn extensions(&self) -> Vec>> { + vec![extension::()] + } +} + +#[tokio::main] +async fn main() -> anyhow::Result<()> { + nexum_launch::run("shepherd", ShepherdRuntime).await +} diff --git a/docker-compose.soak.yml b/docker-compose.soak.yml index 0921f186..b48a9027 100644 --- a/docker-compose.soak.yml +++ b/docker-compose.soak.yml @@ -1,7 +1,7 @@ # docker-compose.soak.yml — 7-day grant stability soak. # # Two services: -# engine — the nexum binary; restarts automatically on crash. +# engine — the shepherd binary; restarts automatically on crash. # snapshotter — Alpine loop that scrapes /metrics every hour and # writes evidence files to docs/operations/soak-reports/. # diff --git a/justfile b/justfile index b1311631..de542480 100644 --- a/justfile +++ b/justfile @@ -1,6 +1,7 @@ -# Build the host engine +# Build the engine binaries: the bare `nexum` engine and the cow +# composition root `shepherd`. build-engine: - cargo build -p nexum-cli + cargo build -p nexum-cli -p shepherd # Build the example WASM module build-module: @@ -38,7 +39,7 @@ build-m2: # --pretty-logs keeps the runbook-friendly human-readable formatter; # production deploys omit the flag and emit JSON. run-m2: build-m2 build-engine - cargo run -p nexum-cli -- --engine-config engine.m2.toml --pretty-logs + cargo run -p shepherd -- --engine-config engine.m2.toml --pretty-logs # Build the M3 example modules (price-alert + balance-tracker + stop-loss) # for wasm32-wasip2. @@ -52,7 +53,7 @@ build-m3: # --pretty-logs keeps the runbook-friendly human-readable formatter; # production deploys omit the flag and emit JSON. run-m3: build-m3 build-engine - cargo run -p nexum-cli -- --engine-config engine.m3.toml --pretty-logs + cargo run -p shepherd -- --engine-config engine.m3.toml --pretty-logs # Build the http-probe example module (wasi:http fetch + allowlist # denial demo) for wasm32-wasip2. @@ -69,7 +70,7 @@ build-e2e: build-m2 build-m3 # downstream `jq` filter can mine submitted/dropped/backoff markers # for the e2e report. See `docs/operations/e2e-testnet-runbook.md`. run-e2e: build-e2e build-engine - cargo run -p nexum-cli -- --engine-config engine.e2e.toml + cargo run -p shepherd -- --engine-config engine.e2e.toml # Assert nexum-runtime is venue-agnostic: crate graph, symbol scan, and # the nexum:host WIT leaf. Advisory in CI until the physical cut lands. @@ -80,7 +81,7 @@ check-venue-agnostic: check: cargo check --target wasm32-wasip2 -p example cargo check -p nexum-runtime - cargo check -p nexum-cli + cargo check -p nexum-cli -p shepherd # Run the full CI series locally before pushing. Mirrors # .github/workflows/ci.yml one-to-one: rustfmt, clippy, rustdoc, the diff --git a/scripts/check-venue-agnostic.sh b/scripts/check-venue-agnostic.sh index e3feef76..26d60d3a 100755 --- a/scripts/check-venue-agnostic.sh +++ b/scripts/check-venue-agnostic.sh @@ -1,8 +1,9 @@ #!/usr/bin/env bash -# Venue-agnosticism check for nexum-runtime: the crate graph reaches no -# videre/intent/venue/cow crate, the sources carry no venue symbol, and -# nexum:host resolves as a leaf WIT package. Advisory in CI until the -# physical cut lands; run locally via `just check-venue-agnostic`. +# Venue-agnosticism check for the host layer: no host-layer crate graph +# (runtime, launcher, bare engine) reaches a videre/intent/venue/cow +# crate, the runtime sources carry no venue symbol, and nexum:host +# resolves as a leaf WIT package. Advisory in CI until the physical cut +# lands; run locally via `just check-venue-agnostic`. set -uo pipefail @@ -16,19 +17,22 @@ command -v rg >/dev/null || { echo "ripgrep (rg) is required" >&2; exit 2; } status=0 -# 1. Crate graph: nothing venue-shaped reachable from nexum-runtime -# (normal + build edges; dev-deps stay local to the crate). -if tree="$(cargo tree -p nexum-runtime -e normal,build --all-features --prefix none --locked)"; then - reached="$(printf '%s\n' "$tree" | - awk '{print $1}' | sort -u | rg -i 'videre|intent|venue|cow' || true)" - if [[ -n $reached ]]; then - fail "crate graph reaches: $(tr '\n' ' ' <<<"$reached")" +# 1. Crate graph: nothing venue-shaped reachable from the host-layer +# crates - the runtime, the generic launcher, and the bare engine +# binary (normal + build edges; dev-deps stay local to the crate). +for crate in nexum-runtime nexum-launch nexum-cli; do + if tree="$(cargo tree -p "$crate" -e normal,build --all-features --prefix none --locked)"; then + reached="$(printf '%s\n' "$tree" | + awk '{print $1}' | sort -u | rg -i 'videre|intent|venue|cow' || true)" + if [[ -n $reached ]]; then + fail "$crate crate graph reaches: $(tr '\n' ' ' <<<"$reached")" + else + pass "$crate crate graph clean" + fi else - pass "crate graph clean" + fail "cargo tree failed for $crate" fi -else - fail "cargo tree failed" -fi +done # 2. Symbol scan: no venue vocabulary anywhere in the crate. Word shapes # skip std::borrow::Cow, ProviderError, and "intentional". diff --git a/scripts/e2e-run.sh b/scripts/e2e-run.sh index ce24b343..8ec2f4e1 100755 --- a/scripts/e2e-run.sh +++ b/scripts/e2e-run.sh @@ -7,7 +7,7 @@ # gitignored. # 3. Cleans data/e2e for a fresh local-store. # 4. Builds all 5 modules + the engine. -# 5. Launches nexum via nohup, redirecting stdout/stderr to +# 5. Launches shepherd via nohup, redirecting stdout/stderr to # docs/operations/e2e-reports/engine-.log. JSON logs # (no --pretty-logs) so e2e-report-gen.sh can mine them with jq. # 6. Waits up to 60 s for the `supervisor ready modules=5 chains=1` @@ -52,7 +52,7 @@ log "building 5 modules + engine (this can take a minute on first run)" cargo build -p price-alert --target wasm32-wasip2 --release >/dev/null cargo build -p balance-tracker --target wasm32-wasip2 --release >/dev/null cargo build -p stop-loss --target wasm32-wasip2 --release >/dev/null - cargo build -p nexum-cli --release >/dev/null + cargo build -p shepherd --release >/dev/null ) ts="$(date -u +%Y%m%dT%H%M%SZ)" @@ -63,7 +63,7 @@ start_iso="$(date -u +%Y-%m-%dT%H:%M:%SZ)" log "launching engine — log: $log_file" ( cd "$REPO_ROOT" - nohup "$REPO_ROOT/target/release/nexum" \ + nohup "$REPO_ROOT/target/release/shepherd" \ --engine-config "$REPO_ROOT/engine.e2e.local.toml" \ >"$log_file" 2>&1 & echo $! > "$STATE_FILE.pid.tmp" diff --git a/scripts/load-run.sh b/scripts/load-run.sh index 91cef9b0..af636aef 100755 --- a/scripts/load-run.sh +++ b/scripts/load-run.sh @@ -80,10 +80,10 @@ log "building modules + engine + load-gen (release)" ( cd "$REPO_ROOT" && cargo build --release --quiet \ --target wasm32-wasip2 \ -p twap-monitor -p ethflow-watcher ) -( cd "$REPO_ROOT" && cargo build --release --quiet -p nexum-cli -p load-gen ) +( cd "$REPO_ROOT" && cargo build --release --quiet -p shepherd -p load-gen ) -log "starting nexum (engine.load.toml)" -( cd "$REPO_ROOT" && ./target/release/nexum --engine-config engine.load.toml ) \ +log "starting shepherd (engine.load.toml)" +( cd "$REPO_ROOT" && ./target/release/shepherd --engine-config engine.load.toml ) \ >"$LOG_DIR/engine.log" 2>&1 & ENGINE_PID=$! echo "ENGINE_PID=$ENGINE_PID" >>"$PID_FILE"