# Evaluated by lian-build CLI with: # LIAN_BUILD_NICKEL_IMPORT_PATH=/path/to/lian-build # "defaults/build_directives.ncl" resolves via LIAN_BUILD_ROOT import-path. # "jpl-project.ncl" resolves via LIAN_BUILD_NICKEL_IMPORT_PATH (not relative) so # the justfile can shadow it with a version-patched copy without touching source. # # This file exports ONE directive PER render profile, keyed by profile name. # Nickel 1.16 has no env access, so profile selection happens in provisioning/ # build.nu (which runs check-wasm-needed.nu against site/config/rendering.ncl): # build.nu extracts the matching field and feeds that single directive to # lian-build. The two profiles are genuinely disjoint builds — different base # images, different Dockerfile, different artifacts (pkg/ vs none) — not one # build with a flag. let d = import "defaults/build_directives.ncl" in let project = import "jpl-project.ncl" in # Args common to both Dockerfiles. project.port is the canonical port. let common_args = { RUST_VERSION = "1.95", NICKEL_VERSION = "1.16.0", LAMINA_REGISTRY = "daoreg.librecloud.online/lamina", BIN_NAME = "rustelo-htmx-server", SERVER_PORT = std.string.from_number project.port, } in # leptos-hydration: cargo-leptos build over lamina/leptos + lamina/rustelo, # emits the WASM pkg/ (version-matched to the binary). content-static bakes # content + i18n at compile time. let leptos_args = common_args & { RUSTELO_VERSION = "0.1.0", LEPTOS_VERSION = "0.3.6", NODE_VERSION = "22", LEPTOS_OUTPUT_NAME = "website", BIN_FEATURES = "content-static", } in # htmx-ssr: plain cargo build over lamina/rust (no wasm toolchain, no node/css # stage, no pkg/). Content is served from the PV at runtime, not baked. let htmx_args = common_args & { BIN_FEATURES = "htmx-ssr", } in # Fields identical across profiles: where to push, how to sign/cache/adapt. # image: registry is the primary write surface (reachable from lian-01). # registry.ontoref.dev (project.image_base) is the K8s pull address; it syncs # from registry on-demand per ADR-019. These must not be the same ref — now # true again: write surface is reg.librecloud.online (+ daoreg.librecloud.online # replica), pull address stays registry.ontoref.dev. let shared = { # Direct port 443 — no NodePort bypass needed. The :31527 NodePort form was # a workaround for the Cilium Gateway LB VIP (10.200.3.x) refusing # connections from the fleet's network (2026-07-06); that routing/SNAT gap # was fixed 2026-07-07 (VIP now reassigned, port 443 confirmed reachable # with consistent 200s) and the NodePort no longer even accepts connections. image_ref = "reg.librecloud.online/%{project.domain}/%{project.name}:%{project.image_tag}", adapter = d.make_adapter_ref { kind = 'fleet, nats_url = "nats://10.0.8.5:30422", consumer_identity = "lian-build", subject_prefix = "fleet.libre-forge", memory_hint_gb = 7, }, # Dual push, both mandatory (adr per src/cli/build.rs: "daoreg/buildadm + # reg/regadm" is the canonical two-credential pattern). Each endpoint carries # its own push_credential; resolution of either failing fails the whole push. # reg.librecloud.online and daoreg.librecloud.online's credentials are # SOPS-encrypted for different age identities (libre-wuji, libre-daoshi). # Both push_credentials share the SAME PATH-style secrets_base/key_file # (lian-build schema addition, 2026-07-07) — a `:`-separated search path # covering every known workspace; each credential finds its own file under # whichever candidate has it and decrypts with whichever key matches. Values # come from project.build_secrets, patched in by the justfile from # wuji_secrets_base:secrets_base / wuji_kage:daoshi_kage — never hardcoded here. registry = d.default_zot_registry "reg.librecloud.online" { path = project.build_secrets.registry_push, kind = 'docker_config, secrets_base = project.build_secrets.secrets_base, key_file = project.build_secrets.key_file, } & { replica_endpoints = [ { endpoint = "daoreg.librecloud.online", push_credential = { path = project.build_secrets.registry_push_daoreg, kind = 'docker_config, secrets_base = project.build_secrets.secrets_base, key_file = project.build_secrets.key_file, }, }, ], }, cache = d.ci_cache_policy project.workspace_id, signing = { key_ref = { kind = 'cosign_private_key, path = project.build_secrets.cosign }, }, # sccache DISABLED TEMPORARILY (2026-07-06): the S3 backend below # (wuji-sccache @ fsn1.your-objectstorage.com) was deliberately deleted in # the 2026-07-01/02 Hetzner cost cleanup; the intended replacement (Garage, # libre-wuji) is not yet reachable from the fleet's buildkit runners # (cross-network routing unresolved — see Dockerfile.htmx-ssr). Uncomment # once that's sorted, repointing endpoint/bucket/region/credential at Garage. # sccache = { # bucket = "wuji-sccache", # endpoint = "https://fsn1.your-objectstorage.com", # region = "eu-central-1", # credential = { kind = 's3, path = project.build_secrets.sccache }, # }, } in let make_directive = fun df bargs => d.make_build_directives { workspace = project.workspace_id, artifacts = [ d.make_artifact { image = shared.image_ref, dockerfile = df, context = "..", # parent of lian-build/ = repo root platforms = ["linux/amd64", "linux/arm64"], build_args = bargs, }, ], adapter = shared.adapter, registry = shared.registry, cache = shared.cache, signing = shared.signing, # sccache = shared.sccache, # disabled temporarily — see shared.sccache comment above } in # Dockerfile suffix === profile name, so the path never drifts from the key. { "leptos-hydration" = make_directive "lian-build/Dockerfile.leptos-hydration" leptos_args, "htmx-ssr" = make_directive "lian-build/Dockerfile.htmx-ssr" htmx_args, }