ontoref


# lian-build > 炼 — *alchemical refinement.* Standalone build substrate for ephemeral BuildKit sessions. `lian-build` is a single Rust binary that orchestrates ephemeral remote BuildKit runs against a pluggable orchestrator. Callers (provisioning, vapora, workspace CI) supply intent as `BuildDirectives` in NCL; `lian-build` controls compute provisioning, OCI cache flow, and multi-actor session namespacing. Compute (`hcloud` / `proxmox` / `docker-local`) and registry (`zot` / `harbor` / `ghcr`) are plug-in slots. | | | |---|---| | Crate | `lian-build` (binary), `0.1.0` | | Status | Beta · pre-1.0, schema and CLI surface still mobile | | Edition | 2021 | | ADRs | adr-001 lift-out · adr-002 CLI subcommand discipline · adr-003 Nickel via subprocess | --- ## What it is - An **orchestrator client**, not a buildkitd. It spawns runners through an external HTTP orchestrator (default `http://localhost:9011`), `rsync`s the build context, then drives `buildctl` over SSH. - A **directives consumer**. Schemas live in `schemas/*.ncl`; the Rust types in `src/directives.rs` mirror them and round-trip via `serde_json`. - An **event publisher**. `started` / `completed` / `failed` lifecycle events go to NATS at `..build.` (best-effort — NATS failures never fail a build). ## What it is not - Not a library. There is no `lib.rs`. Public surface is the CLI and the NCL schemas. - Not coupled to provisioning. ADR-001 forbids importing `provisioning`, `platform-config`, or any `stratum-`-prefixed crate (grep-checked). - Not a Nickel runtime. NCL is parsed by shelling out to the `nickel` CLI (ADR-003). `nickel` must be on `$PATH`. --- ## CLI Two subcommands, no default, no flat-arg fallback (ADR-002). All logs go to **stderr**; **stdout** is reserved for one structured envelope per invocation. ### `lian-build build` Run a build on an ephemeral runner. Flat args **or** a directives file: ```bash lian-build build \ --workspace \ --context \ --image \ --ssh-key \ [--directives ] \ [--dockerfile Dockerfile] \ [--cache-from ] [--cache-to ] \ [--language rust|go|java|...] \ [--platforms linux/amd64,linux/arm64] \ [--runner-image ] \ [--orchestrator-url ] \ [--nats-url ] [--nats-nkey-seed ] [--nats-subject-prefix

] ``` When `--directives ` is supplied it takes precedence over flat-arg fields except `--ssh-key`, which is still required separately (directives don't yet carry an inline SSH reference). Environment fallbacks: `BUILDKIT_WORKSPACE`, `BUILDKIT_SSH_KEY`, `BUILDKIT_RUNNER_IMAGE`, `ORCHESTRATOR_URL`, `NATS_URL`, `NATS_NKEY_SEED`, `NATS_SUBJECT_PREFIX`. ### `lian-build integrate` Federated probe. Reads a `SecretDeliveryContext` JSON envelope from stdin, emits a `ResultEnvelope` JSON line on stdout, optionally publishes a completion event to NATS. ```bash echo '' | lian-build integrate \ [--nats-url ] [--nats-nkey-seed ] ``` Omit `--nats-url` to skip event emission with a warning (the envelope still goes to stdout). --- ## Three-tier runner sizing `sizing::resolve` walks three sources, first match wins: 1. **Explicit** — `.build-spec.ncl` in the build context, validated against `schemas/build_spec.ncl` (`bounded_cpu_` ≤ 256, `bounded_time_budget_` ≤ 1440 min). 2. **P95 historical** — `OrchestratorClient::get_p95(workspace)` returns measured CPU / memory P95 from prior runs; multiplied by 1.2, floored at `min(2 cpu, 4 GB)`. 3. **Language defaults** — `RunnerSize::language_default(lang)`: | Language | CPU | Memory | Time budget | |------------------------|-----|--------|-------------| | `rust` | 4 | 8 GB | 60 min | | `go` | 2 | 4 GB | 30 min | | `java` / `kotlin` / `scala` | 4 | 8 GB | 45 min | | *(default)* | 2 | 4 GB | 30 min | On exit-code `137` or stderr containing `OOM` / `Killed`, the build retries **once** at the next tier (`cx22` → `cx32` → `cx42` → `cx52`). The retry cap is hard-bound (`retry::MAX_OOM_RETRIES = 1`). --- ## Cache namespacing Two layers, defined in `schemas/cache_policy.ncl` and enforced in `src/cache.rs`: - `ci//*` — canonical, written by CI, **read-only** to sessions. - `dev/-/*` — ephemeral per-session actor. Sessions read from both layers; CI never imports from `dev/*`. These invariants are guarded by tests under `src/cache.rs`. --- ## NCL contract surface | Schema | Defines | |-------------------------------|-------------------------------------------------------------------------| | `schemas/build_directives.ncl`| `BuildDirectives`, `BuildArtifact`, `ComputeProviderRef`, `RegistryProviderRef`, `RunnerOverride`, `NatsEventConfig` | | `schemas/build_spec.ncl` | `BuildSpec` (per-repo `.build-spec.ncl`) | | `schemas/cache_policy.ncl` | `CachePolicy`, `BuildMode`, `SessionActor`, `SessionCacheDisposition` | | `schemas/build_result.ncl` | `BuildResult` envelope shape | | `schemas/vault_refs.ncl` | `VaultCredRef`, `VaultKeyRef` | | `defaults/build_directives.ncl` | `make_*` constructors, `ci_cache_policy` / `session_cache_policy` helpers | Validate with the `nickel` CLI: ```bash nickel export \ --import-path /Users/Akasha/Development/lian-build \ --import-path /Users/Akasha/Development/lian-build/schemas \ --import-path /Users/Akasha/Development/ontoref \ --import-path /Users/Akasha/Development/ontoref/ontology \ schemas/build_directives.ncl ``` --- ## Hard constraints (ADR-001) These two rules are grep-checked and define the lift-out boundary: 1. **`no-provisioning-lib-import`** — `Cargo.toml` and `src/` must not match `platform-config|provisioning|stratum-`. The `platform-nats` path-dep from `stratumiops` is explicitly allowed; the constraint targets the `provisioning` workspace and `stratum-`-prefixed crates. 2. **`build-directives-ncl-vocabulary`** — `src/` must not match `provisioning_workspace|vapora_|woodpecker_`. Caller-specific logic stays in caller-supplied directives, not in core. --- ## Build / test / run ```bash cargo build # debug cargo build --release # release at target/release/lian-build cargo clippy -- -D warnings # mandatory before commit cargo fmt cargo test # full suite cargo test # single test by substring cargo test -- --nocapture # show tracing in tests ``` `just` recipes live in `justfiles/{build,test,ci}.just`. Run `just` (or `just help`) to list them. ### Stratumiops peer dependency `platform-nats` is consumed as a local path dependency from `/Users/Akasha/Development/stratumiops/crates/platform-nats` (declared directly in `Cargo.toml`). That path must exist for `cargo build` to succeed — there is no feature flag to disable it. --- ## Module layout ``` src/ main.rs # CLI parsing, top-level orchestration, OOM-retry control flow buildctl_runner.rs # rsync_context + run_buildctl over SSH; OOM_EXIT_CODE = 137 cache.rs # BuildMode, build_cache_flags, ci/* vs dev//* invariants directives.rs # BuildDirectives ↔ JSON via `nickel export` subprocess integration/ # federated probe handler (stdin → ResultEnvelope on stdout) context.rs · event.rs · handler.rs · result.rs · mod.rs nats_events.rs # BuildEventPublisher over platform-nats::EventStream orchestrator_client.rs # HTTP client: spawn_runner, destroy_runner, get_p95, record_metrics retry.rs # MAX_OOM_RETRIES = 1; SIZE_TIERS walk (cx22→cx32→cx42→cx52) sizing.rs # three-tier resolution ``` Operational surface: ``` adrs/ # accepted ADRs (NCL) .ontology/ # core, state, gate, manifest — design intent reflection/ # modes, backlog, qa schemas/ # caller-facing NCL contracts defaults/ # constructor / helper NCL catalog/{domains,modes}/ # federated peer publishing layout examples/sample.ncl # example BuildDirectives instance tests/fixtures/ # integration test fixtures .coder/ # session interaction files (not product docs) .claude/ # operational config (symlinks into shared dev-system) ``` --- ## Further reading - `adrs/adr-001-lian-build-as-standalone.ncl` — why this project exists, alternatives rejected, the two grep-checked invariants. - `adrs/adr-002-cli-subcommand-discipline.ncl` — subcommand-only surface, stderr/stdout discipline. - `adrs/adr-003-nickel-via-subprocess.ncl` — why `nickel` is on `$PATH`, not in `Cargo.toml`. - `.ontology/core.ncl` — axioms (ephemeral-builds, provider-pluggability, cache-content-addressed, caller-supplies-directives), tensions, practices. - `.ontology/state.ncl` — five maturity dimensions and their current transitions (provider-pluggability, session-multi-actor, active-active-registry, caller-integration, peer-publishing). - `CHANGES.md` — record of accepted decisions and visible surface changes.