No description
  • Rust 77.5%
  • HTML 11.8%
  • Nickel 8%
  • Just 2.7%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
claude-bot e23e90f3e9
init: lian-build — build substrate for ephemeral BuildKit sessions
Fresh history. The previous repository published runtime state (daemon task
records with server addresses and root ssh invocations) and carried one
operator's infrastructure inside what is meant to be a caller-agnostic
substrate. Rewriting history would have left those blobs reachable by SHA on
the forge, so the repository was recreated instead of scrubbed.

What this repo is, and what it deliberately is not:

- Vocabulary: `schemas/` (BuildDirectives, BuildSpec, CachePolicy, vault refs)
  and `defaults/` constructors. This is the caller-facing contract.
- Implementation: the `lian-build` binary — directive parsing, three adapter
  kinds ('local pre-flight, 'remote endpoint, 'fleet NATS claim with per-claim
  ephemeral X25519 age TLS), cache namespacing, manifest-index merge, daemon
  and web UI.
- No infrastructure. Per adr-009 and the axiom `caller-supplies-directives`,
  no registry host, fleet name, subject prefix, network or credential path is
  compiled in or committed. `examples/` demonstrates each adapter kind with
  placeholder values only; the concrete bindings live in the caller's own repo.

Two infrastructure defaults that used to be hardcoded are now required from
the caller: `adapter.subject_prefix` for 'fleet builds, and AUX_PULL_REGISTRY
(formerly a built-in lamina registry host) for the base-image auth entry.

Design intent — ontology, ADRs, reflection, catalog — lives in the
constellation spine at `../.ontoref/`, not here.
2026-08-08 22:38:05 +01:00
.cargo init: lian-build — build substrate for ephemeral BuildKit sessions 2026-08-08 22:38:05 +01:00
.config init: lian-build — build substrate for ephemeral BuildKit sessions 2026-08-08 22:38:05 +01:00
assets init: lian-build — build substrate for ephemeral BuildKit sessions 2026-08-08 22:38:05 +01:00
defaults init: lian-build — build substrate for ephemeral BuildKit sessions 2026-08-08 22:38:05 +01:00
examples init: lian-build — build substrate for ephemeral BuildKit sessions 2026-08-08 22:38:05 +01:00
justfiles init: lian-build — build substrate for ephemeral BuildKit sessions 2026-08-08 22:38:05 +01:00
schemas init: lian-build — build substrate for ephemeral BuildKit sessions 2026-08-08 22:38:05 +01:00
src init: lian-build — build substrate for ephemeral BuildKit sessions 2026-08-08 22:38:05 +01:00
tests/fixtures init: lian-build — build substrate for ephemeral BuildKit sessions 2026-08-08 22:38:05 +01:00
.gitignore init: lian-build — build substrate for ephemeral BuildKit sessions 2026-08-08 22:38:05 +01:00
.pre-commit-config.yaml init: lian-build — build substrate for ephemeral BuildKit sessions 2026-08-08 22:38:05 +01:00
card.ncl init: lian-build — build substrate for ephemeral BuildKit sessions 2026-08-08 22:38:05 +01:00
Cargo.lock init: lian-build — build substrate for ephemeral BuildKit sessions 2026-08-08 22:38:05 +01:00
Cargo.toml init: lian-build — build substrate for ephemeral BuildKit sessions 2026-08-08 22:38:05 +01:00
CHANGELOG.md init: lian-build — build substrate for ephemeral BuildKit sessions 2026-08-08 22:38:05 +01:00
images-time.md init: lian-build — build substrate for ephemeral BuildKit sessions 2026-08-08 22:38:05 +01:00
justfile init: lian-build — build substrate for ephemeral BuildKit sessions 2026-08-08 22:38:05 +01:00
manifest.ncl init: lian-build — build substrate for ephemeral BuildKit sessions 2026-08-08 22:38:05 +01:00
README.md init: lian-build — build substrate for ephemeral BuildKit sessions 2026-08-08 22:38:05 +01:00

ontoref


lian-build

炼 — alchemical refinement. Standalone build substrate for ephemeral BuildKit sessions.

lian-build is a single Rust binary that drives ephemeral BuildKit builds. It resolves a buildkitd endpoint from caller-supplied BuildDirectives, drives buildctl against it, pushes the image, and emits NATS lifecycle events.

Per adr-009, lian-build has no provider knowledge: it does not spawn VMs, hold cloud tokens, or manage SSH keys, networks, or firewalls. It only knows a buildkitd endpoint and the TLS to reach it. Resource acquisition belongs to whoever owns the infrastructure — fleet-daemon, the operator's local daemon, or a remote endpoint procured out-of-band.

Callers (provisioning, lamina, vapora, workspace CI) supply intent as BuildDirectives in NCL; lian-build controls execution and OCI cache flow.

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 · adr-003 Nickel via subprocess · adr-004 local tier · adr-005 provider catalog (superseded by adr-009) · adr-006 catalog structure · adr-007 extra contexts · adr-008 ontoref-foundation migration · adr-009 no provider knowledge

What it is

  • An endpoint resolver + buildctl driver. AdapterRef.kind selects the buildkitd: 'local (on-host daemon), 'remote (external endpoint + TLS), or 'fleet (NATS claim against a declared fleet, per-claim-encrypted TLS). A single dispatch path (run_buildctl_lite) streams the context via gRPC.
  • A pre-flight gate. For 'local, lian-build queries host memory/CPU (sysinfo) and fails fast if the directives spec block exceeds capacity.
  • 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 <prefix>.<workspace>.build.<event> (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 a VM orchestrator. Per adr-009 it spawns no compute on any path — no orchestrator HTTP client, no SSH runner, no provider catalog.
  • 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.

The tetra-build ecosystem

lian-build is one of four standalone projects that together form tetra-build — the ecosystem's container build pipeline. Each project is versioned and released on its own; they connect through declared contracts (NCL directives, NATS subjects, OCI artifacts), never through library linkage.

Project Role Relation to lian-build
forge-fleet Fleet control plane — claim-arbitration daemon + node agent Supplies the buildkitd endpoint behind the 'fleet adapter kind
lian-build Build driver — resolves an endpoint, drives buildctl, emits events (this project)
lamina Build-layer catalog — toolchain + cargo-chef pre-cooked dep layers A caller — supplies BuildDirectives; its layers feed builds as extra_contexts
solera Runtime-image catalog — deployable service images A caller — builds its images via lian-build build --directives

The shared goal (the common CTA): fast, reproducible, ephemeral OCI builds. The specialization: forge-fleet owns where builds run, lian-build owns how they run, lamina owns what goes in, solera owns what comes out. adr-009 draws the line precisely — lian-build holds no provider knowledge, so forge-fleet can appear, change, or be replaced with zero lian-build changes.


Adapter kinds (adr-009)

BuildDirectives.adapter.kind is exactly [| 'local, 'remote, 'fleet |]:

  • 'local — buildkitd on the same host. lian-build runs a host-capacity pre-flight against BuildSpec.memory_gb_min / cpu_min before dispatch. buildkit_addr overrides the default socket.
  • 'remote — an externally-supplied buildkitd endpoint plus a tls_bundle_ref (SOPS-encrypted {ca, cert, key} PEM bundle). lian-build connects and builds; it does not know what runs the endpoint.
  • 'fleet — lian-build emits a NATS claim against a declared fleet_ref, generates a fresh ephemeral X25519 age keypair, receives a per-claim-encrypted TLS grant, builds, and releases the claim. Resolves to 'remote internally.

CLI

All subcommands, no default, no flat-arg fallback (ADR-002). All logs go to stderr; stdout carries structured output (envelopes, JSON, tables).

Daemon-facing subcommands (project, runner, trigger, status, builds, adapter) default to http://localhost:19012 and can be overridden via LIAN_BUILD_DAEMON_URL.

lian-build build

Run a build. A BuildDirectives NCL file is the primary input; flat args are accepted for the directives-less smoke-test path ('local only, no spec).

lian-build build \
  --directives <file.ncl> \
  [--context <dir>] \
  [--platforms linux/amd64,linux/arm64] \
  [--cache-from <ref>] [--cache-to <ref>] \
  [--nats-url <url>] [--nats-nkey-seed <seed>] [--nats-subject-prefix <p>] \
  [--secrets-base <dir>]

The adapter block in the directives selects 'local / 'remote / 'fleet. --secrets-base points at the SOPS secrets root used to resolve registry push credentials, remote TLS bundles, and fleet NKey seeds.

Env: BUILDKIT_WORKSPACE, NATS_URL, NATS_NKEY_SEED, NATS_SUBJECT_PREFIX, LIAN_BUILD_SECRETS_BASE.

lian-build serve

Start the lifecycle daemon — HTTP API, project registry, TTL sweeper, optional web UI.

lian-build serve \
  [--config <file.ncl|.toml|.json>] \
  [--port 19012] \
  [--state-file <path>] \
  [--config-dir <dir>] \
  [--data-dir <dir>] \
  [--templates-dir <dir>] \
  [--admin-key <key>] \
  [--nats-url <url>]

Env: LIAN_BUILD_DAEMON_CONFIG, LIAN_BUILD_DAEMON_PORT, LIAN_BUILD_STATE_FILE, LIAN_BUILD_CONFIG_DIR, LIAN_BUILD_DATA_DIR, LIAN_BUILD_TEMPLATES_DIR, LIAN_BUILD_ADMIN_KEY.

lian-build trigger

Trigger a build for a registered project via the daemon API. Blocks until the build reaches a terminal state (override with --no-wait).

lian-build trigger <slug> \
  --adapter <adapter-name> \
  [--platform linux/arm64] \
  [--stage <stage>] \
  [--cache-from <ref>] [--cache-to <ref>] \
  [--keep] [--no-wait] [--poll-interval 2] \
  [--format json|id]

--format id prints only the build UUID — useful for scripting: BUILD_ID=$(lian-build trigger myproject --adapter provisioning --format id).

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.

echo '<context-json>' | lian-build integrate \
  [--nats-url <url>] [--nats-nkey-seed <seed>]

lian-build project

Manage registered projects (daemon must be running).

lian-build project register <slug> <directives-path> [--root <dir>] [--workspace <name>]
lian-build project list
lian-build project status <slug>
lian-build project remove <slug>

lian-build runner

Inspect and manage active runners.

lian-build runner list
lian-build runner pin <id>
lian-build runner unpin <id>
lian-build runner destroy <id>

lian-build adapter

List configured daemon runner adapters and run live health probes.

lian-build adapter [--format table|json]

lian-build builds

List and clear build records stored by the daemon.

lian-build builds list [--project <slug>] [--status queued|running|completed|failed] \
                       [--adapter <name>] [--older-than <secs>] [--limit <n>]
lian-build builds clear [--project <slug>] [--status <s>] [--adapter <name>] [--older-than <secs>]

lian-build events

Stream live NATS build/runner events to stdout.

lian-build events --nats-url <url> \
  [--nats-nkey-seed <seed>] \
  [--subject-prefix lian-build] \
  [--subject <pattern>] \
  [--format json|human]

lian-build status

Print a daemon status summary (runners, projects, adapters) to stdout.

lian-build status [--daemon-url <url>]

Pre-flight host capacity (adr-009)

When adapter.kind = 'local, lian-build runs a pre-flight before any buildkitd traffic. src/preflight.rs::check_local_capacity reads host memory and logical CPU count via the sysinfo crate and compares them against the directives spec block:

spec = { memory_gb_min = 16, cpu_min = 8 },

If the host is under-spec the build fails fast with a clear PreflightError that suggests switching the adapter to 'fleet. A directive with no spec block is a no-op. 'remote defers sizing to the caller; 'fleet defers it to fleet-daemon's claim arbitration — the gate is scoped to 'local.


Cache namespacing

Two layers, defined in schemas/cache_policy.ncl and enforced in src/cache.rs:

  • ci/<workspace>/* — canonical, written by CI, read-only to sessions.
  • dev/<actor-id>-<workspace>/* — 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 (incl. extra_contexts), AdapterRef ('local/'remote/'fleet), RegistryProviderRef, SigningConfig, NatsEventConfig, SccacheConfig
schemas/build_spec.ncl BuildSpec — host resource floor (memory_gb_min, cpu_min) for the 'local pre-flight
schemas/build_result.ncl BuildResult envelope shape
schemas/cache_policy.ncl CachePolicy, BuildMode, SessionActor, SessionCacheDisposition
schemas/vault_refs.ncl VaultCredRef, VaultKeyRef
defaults/build_directives.ncl make_* constructors, ci_cache_policy / session_cache_policy helpers

Validate with the nickel CLI:

nickel typecheck --import-path . schemas/build_directives.ncl
nickel export    --import-path . schemas/build_spec.ncl

Hard constraints (ADR-001)

These two rules are grep-checked and define the lift-out boundary:

  1. no-provisioning-lib-importCargo.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-vocabularysrc/ must not match provisioning_workspace|vapora_|woodpecker_. Caller-specific logic stays in caller-supplied directives, not in core.

ADR Hard constraints across all ADRs are checked by ontoref adr validate.


Build / test / run

cargo build                          # debug
cargo build --release                # release at target/release/lian-build
cargo clippy --all-targets -- -D warnings   # mandatory before commit
cargo fmt
cargo test                           # full suite
cargo test <name>                    # single test by substring

just recipes live in justfiles/{build,test,ci,catalog,services}.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 entry — Cli/Commands, mod declarations, dispatch
  cli/                 # one handler module per subcommand
    args.rs            #   all clap arg structs
    build.rs           #   run_build_command — adapter-kind dispatch, pre-flight, fleet claim
    serve.rs · trigger.rs · project.rs · runner.rs · status.rs
    events.rs · builds.rs · adapter.rs
  preflight.rs         # check_local_capacity — sysinfo host-capacity gate for 'local
  buildctl_runner.rs   # run_buildctl_lite — drives buildctl against (addr, optional TLS)
  fleet_client.rs      # FleetClient — NATS claim flow, per-claim X25519 age TLS delivery
  directives.rs        # BuildDirectives ↔ JSON via `nickel export` subprocess
  cache.rs             # BuildMode, build_cache_flags, ci/* vs dev/<actor>/* invariants
  project.rs           # project registry and build records; JSON persistence
  api_catalog.rs       # HTTP handlers for catalog domain/mode queries
  nats_events.rs       # BuildEventPublisher over platform-nats
  nats_subscriber.rs   # NATS event subscriber for the `events` subcommand
  daemon/
    mod.rs             #   lifecycle daemon — runner state, TTL enforcement, NATS events
    adapter.rs         #   Adapter, AdapterStatus, concurrent health_check_all
    config.rs          #   DaemonConfig loading (.ncl / .toml / .json)
    server.rs          #   Axum HTTP router; all REST handlers
    state.rs           #   AppState (runners, projects, builds, adapters, NATS, Tera)
    sweeper.rs         #   TTL enforcement background task
  integration/         # federated probe handler (stdin → ResultEnvelope on stdout)
  ui/                  # Tera + HTMX web UI served by the daemon

Operational surface:

schemas/                           # caller-facing NCL contracts (build_directives, build_spec, …)
defaults/                          # constructor / helper NCL
examples/                          # local / remote / fleet BuildDirectives, all placeholder values
tests/fixtures/                    # integration test fixtures

../.ontoref/                       # constellation spine — design intent, not shipped code
  ontology/                        #   core, state, gate, manifest
  adrs/                            #   accepted ADRs (NCL), adr-001 through adr-009
  reflection/                      #   modes, backlog, qa
  catalog/                         #   adapter runner scripts, domain contracts

Further reading

  • ../.ontoref/adrs/adr-001-lian-build-as-standalone.ncl — why this project exists, alternatives rejected, the two grep-checked invariants.
  • ../.ontoref/adrs/adr-002-cli-subcommand-discipline.ncl — subcommand-only surface, stderr/stdout discipline.
  • ../.ontoref/adrs/adr-003-nickel-via-subprocess.ncl — why nickel is on $PATH, not in Cargo.toml.
  • ../.ontoref/adrs/adr-004-local-build-tier.ncl — local build tier; three-tier isolation model.
  • ../.ontoref/adrs/adr-005-compute-provider-catalog.nclsuperseded by adr-009. The in-daemon provider catalog with health probes.
  • ../.ontoref/adrs/adr-006-catalog-structure.ncl — two-layer catalog; provisioning imports via --import-path bridge.
  • ../.ontoref/adrs/adr-007-extra-contexts-named-buildkit-contexts.nclBuildArtifact.extra_contexts; extra-ctx-<name> gRPC local-id; is_local_context detection rule.
  • ../.ontoref/adrs/adr-008-ontoref-foundation-migration.ncl — auth/ui migration to the ontoref-foundation sibling crates (the compute integration was retired by adr-009).
  • ../.ontoref/adrs/adr-009-no-provider-knowledge.nclAdapterRef.kind reduced to 'local/'remote/'fleet; provider catalog and orchestrator client removed; 'local pre-flight; per-claim ephemeral X25519 age TLS for 'fleet.
  • ../.ontoref/ontology/core.ncl — axioms, tensions, practices, and named caller nodes.
  • ../.ontoref/ontology/state.ncl — five maturity dimensions and their transitions.
  • CHANGELOG.md — record of accepted decisions and visible surface changes.