# orchestrate — guide
> **Class:** `guide` · audience: a techie bringing up a lake of products. The *what* is in
> [`orchestrate-design.md`](orchestrate-design.md).
## Compute the bring-up order
```rust
use skidbladnir::{orchestrate, product::ComponentSpec};
let products: Vec<_> = [ComponentSpec::nornir_server(), ComponentSpec::holger()]
.iter().map(|c| c.as_product()).collect();
let waves = orchestrate::start_waves(&products)?; // Vec<Vec<String>>
// e.g. [["nornir-server"], ["holger"]] — holger's `after nornir-server` puts it in a later wave
```
Or straight from a whole-lake spec:
```rust
use skidbladnir::{config::LakeSpec, pki::PkiSource, product::ComponentSpec};
let lake = LakeSpec {
version: "20260704".into(),
pki: PkiSource::SelfGenerated,
products: vec![ComponentSpec::nornir_server(), ComponentSpec::holger()],
};
let waves = lake.start_waves()?; // lib.rs:137
```
## Start each wave (concurrency within a wave)
```rust
for wave in waves {
std::thread::scope(|s| {
for product in &wave {
s.spawn(move || { /* install::apply / start this product */ });
}
});
}
```
Products in the same wave have no ordering constraint between them, so start them concurrently;
wave `i+1` only begins once wave `i` is up.
## Tear down in reverse
```rust
let order = orchestrate::teardown_order(&products)?; // dependents before their dependencies
```
## Emit telemetry to the warehouse
```rust
use skidbladnir::events::{JsonlSink, EventEmitter, op, phase, status};
let sink = JsonlSink::under_warehouse(&warehouse_root)?; // <root>/airgap_events.jsonl
let em = EventEmitter::new("run-2026-07-04", &sink);
em.emit("holger", op::INSTALL, phase::START, status::RUNNING, "unpacking", &["nornir-server".into()]);
// … later …
em.emit("holger", op::START, phase::END, status::OK, "healthy", &[]);
```
Read them back (sorted by `seq`) with `skidbladnir::events::read_jsonl(&path)?`.
## Gotchas
- A **dependency cycle** is a hard error naming the stuck products — fix the `after` edges.
- Deps that name something outside your product set (`network.target`) are ignored, not errors —
they are init-system concerns, not products.
- Emission is **best-effort**: a sink failure logs to stderr and never aborts the bring-up. The
events are observability, not a gate.
- The sink writes under the **warehouse root**, never `/tmp` — pass a real warehouse path.