diff --git a/dag/extdeps/printing/orca_slicer.dag b/dag/extdeps/printing/orca_slicer.dag index e4ae0bef505..7a2fccbe32e 100644 --- a/dag/extdeps/printing/orca_slicer.dag +++ b/dag/extdeps/printing/orca_slicer.dag @@ -2,7 +2,13 @@ module extdeps.printing.orca_slicer import extdeps.exec.program { uncataloged_program } -import std.types { String, NonEmptyStr, List } +import std.types { String, NonEmptyStr, List, FilePath } +import extdeps.shell +import extdeps.shell.exec { ProcessOutcome, ProcessOutputPresent, ProcessOutputAbsent, ProcessRefused } +import extdeps.tools.sha256sum { sha256sum_file_digest_via_shell, Sha256FileDigest, Sha256FileDigestUnavailable } +import extdeps.tools.chmod { chmod_set_mode_command, chmod_path_resolved_program } +import gunbc.command_runner { run_shell_command_capture } +import extdeps.exec.command { LocalExec } import std.nat { Nat } import std.decl_ref { DeclarationRef, WholeDeclaration } import extdeps.external_authority { ExternalAuthority, ExternalModelScope, ExternalSubjectRef } @@ -80,49 +86,12 @@ data orca_slicer_linux_aarch64_appimage_sha256: NonEmptyStr = "e1a07275a25f17662 // clearing the floor as "runs" is overstating its own measurement. data orca_slicer_required_glibc: GlibcVersion = glibc_version(major: 2, minor: 38) -// THE EXECUTABLE IS ITS OWN SUBJECT, SEALED, AND THIS MODULE IS THE ONLY PLACE THAT CAN SAY WHAT -// COUNTS AS ORCA. A consumer that took a bare ArgvCommand could run `true`, observe a genuine zero -// exit, and carry the result forward as evidence that a slicer executes -- generic executability -// read as ORCA executability. The subject was not substituted there and the receipt was not faked; -// the executed thing was simply of the wrong kind, which no amount of care at the consumer can see. -// -// The kind is therefore constructional rather than checked: OrcaExecutable has one constructor, so a -// caller cannot spell an Orca invocation over some other program. What that constructor CONSUMES is -// settled below, and today the answer is that nothing does. That is DESIGN section 3 as much as -// section 5: what counts as this upstream product is a fact this module owns, and a consumer -// comparing a program string to "orca-slicer" would be a second authority for it -- and a string -// check besides, which the AppImage's own filename already shows is unreliable. -// -// AND THERE IS NO MINT, WHICH IS THE HONEST STATE RATHER THAN A GAP. -// -// The kind question was real: `true --version` exits zero on every host, so a consumer taking a bare -// ArgvCommand could read generic executability as ORCA executability. Sealing the executable answers -// it, and the mint must consume evidence rather than a string -- an unrestricted -// `orca_executable(program: NonEmptyStr)` would hand back the very authority the seal holds, and -// restricting its CALLERS only moves that one function outward. -// -// A DIGEST CHECK AT MINT TIME IS NOT ENOUGH, and this is where the previous revision stopped. It -// verified the bytes at a path and then stored THE PATH. A path is a mutable locator, not the -// content identity that earned the seal: verify the real AppImage, receive the seal, replace the -// file, and the capability is spent against whatever now occupies that path. True when minted, false -// when used. That matters most in this module, which insists the 2.38 floor belongs to THIS -// content -- so a seal that decays to a name reintroduces exactly the inheritance it refuses. -// -// gunbc.package_delivery already refuses to treat a staging path as durable receipt identity, and -// the Spark runtime publishes into a root derived from materialization identity rather than trusting -// an arbitrary mutable path. Either shape would close this: re-verify at every positive spend, or -// materialize into an owned content-addressed realization the probe and the slice both consume. -// -// NEITHER BELONGS IN THIS PR, so the positive capability stays UNINHABITED. There is no way to -// obtain an OrcaExecutable, and therefore no way to reach ExecutionReady or a SlicePlan. The direct- -// host route keeps what it actually established -- the measured ABI floor, and two recorded hosts -// that cleared it and still refused -- which is useful and honest on its own. A coproduct having a -// Ready arm is not a reason to manufacture a way to reach it; that is the same fabrication as the -// three slicing witnesses this PR deleted and the export positive control it just deleted. -// -// NEXT-RUNG TRIGGER: a content-addressed materialization of the selected asset, from which the -// executable capability is named and which the probe and slice invocations both consume, so the -// bytes that were verified are the bytes that run. +// A capability may be spent only inside with_verified_orca's private-copy lifetime. +// The archive is copied first and the copy is checked against the selected asset's digest. +// The callback can return only a process result, never the capability. Both probe and slice +// execute that same copy through AppImage's extract-and-run runtime, which removes its extraction. +// The private realization protects against replacing the caller's original archive. Like the +// printer payload bracket, it does not defend against a hostile process running as this user. type OrcaExecutable sole_constructor { program: NonEmptyStr } fn orca_executable_program(e: OrcaExecutable) -> NonEmptyStr { e.program } @@ -131,7 +100,7 @@ fn orca_executable_program(e: OrcaExecutable) -> NonEmptyStr { e.program } // cheapest Orca-shaped command there is, it needs no display, and it exits zero only if the runtime // closure resolved -- which is exactly the fact a would-be slicer needs before a slice is planned. fn orca_version_probe_command(executable: OrcaExecutable) -> ArgvCommand { - argv_command(program: uncataloged_program(invocation: executable.program), arguments: ["--version"]) + argv_command(program: uncataloged_program(invocation: executable.program), arguments: ["--appimage-extract-and-run", "--help"]) } // THE CLI, WHICH IS THE ONLY INTERFACE THIS REPOSITORY USES. The desktop application is the same @@ -147,6 +116,8 @@ fn orca_slice_to_project_command( argv_command( program: uncataloged_program(invocation: executable.program), arguments: [ + "--appimage-extract-and-run", + "--arrange", "1", "--orient", "0", "--ensure-on-bed", "--slice", "0", "--load-settings", concat(concat(printer_profile, ";"), process_profile), "--load-filaments", filament_profile, @@ -155,3 +126,28 @@ fn orca_slice_to_project_command( ], ) } + +// The caller's copy is never executed, and a failed digest cannot mint the capability. +fn with_verified_orca(appimage: FilePath, use: fn(OrcaExecutable) -> ProcessOutcome) -> ProcessOutcome { + let temp = shell.Mktemp.Dir() + if !temp.success { return ProcessRefused { exit_code: 1, stderr: "Cannot create private Orca realization" } } + let path = join([temp.path as String, "/", orca_slicer_linux_aarch64_appimage_sha256 as String, ".AppImage"], "") + let result = if !shell.Copy.File(source: appimage, destination: path as FilePath).success { + ProcessRefused { exit_code: 1, stderr: "Cannot copy Orca archive" } + } else { + match sha256sum_file_digest_via_shell(path: path as NonEmptyStr) { + Sha256FileDigestUnavailable { path: _, reason: r } => ProcessRefused { exit_code: 1, stderr: r as String } + Sha256FileDigest { digest: d } => + if (d.hex as String) != (orca_slicer_linux_aarch64_appimage_sha256 as String) { + ProcessRefused { exit_code: 1, stderr: "Orca archive differs from the selected upstream digest; not executed" } + } else { + match run_shell_command_capture(command: chmod_set_mode_command(chmod_program: chmod_path_resolved_program, mode: "0500", path: path), transport: LocalExec) { + ProcessRefused { exit_code: c, stderr: e } => ProcessRefused { exit_code: c, stderr: e } + _ => use(OrcaExecutable { program: path as NonEmptyStr }) + } + } + } + } + if shell.Remove.RecursiveForce(path: temp.path as FilePath).success { result } + else { ProcessRefused { exit_code: 1, stderr: concat("Orca realization cleanup failed at ", temp.path as String) } } +} diff --git a/dag/gunbc/product/printed_chassis/slice_run.dag b/dag/gunbc/product/printed_chassis/slice_run.dag new file mode 100644 index 00000000000..068a1b681a2 --- /dev/null +++ b/dag/gunbc/product/printed_chassis/slice_run.dag @@ -0,0 +1,56 @@ +module product.printed_chassis.slice_run + +import std.types { String, FilePath, List } +import std.process { ProcessExit, ExitSuccess, exit_failure } +import extdeps.printing.orca_slicer { with_verified_orca, orca_version_probe_command, orca_slice_to_project_command } +import extdeps.exec.command { LocalExec } +import extdeps.shell.exec { ProcessRefused } +import gunbc.command_runner { run_shell_command_capture } +import extdeps.languages.json.parse { parse_json_document, JsonDocumentParsed } +import extdeps.filesystem.filesystem_io { Filesystem } +import extdeps.languages.json.emit { JsonKeyValue, JsonObject, json_object, json_kv, json_string, serialize_json } + +// Selected slicing settings, not a claim of live printer telemetry. The 2 mm brim +// keeps the 170.94 mm gauge inside the 180 mm envelope without scaling the gauge. +fn fit_prototype_settings_members() -> List { + [ + json_kv(key: "type", value: json_string(s: "process")), + json_kv(key: "from", value: json_string(s: "user")), + json_kv(key: "name", value: json_string(s: "Unpowered fit prototype 0.20 mm PLA")), + json_kv(key: "curr_bed_type", value: json_string(s: "Textured PEI Plate")), + json_kv(key: "brim_type", value: json_string(s: "outer_only")), + json_kv(key: "brim_width", value: json_string(s: "2")), + json_kv(key: "skirt_loops", value: json_string(s: "0")), + json_kv(key: "enable_support", value: json_string(s: "0")), + ] +} + +// Runs the exact pinned upstream artifact over explicitly supplied model and profiles. +// A successful process result must still be followed by project/G-code readback before printing. +fn run(appimage: String, model: String, output: String, machine: String, filament: String, process: String) -> ProcessExit { + let settings = concat(output, ".settings.json") + let original = Filesystem.Read(path: process) + if !original.success { return exit_failure(reason: "Cannot read base process profile") } + let overrides = fit_prototype_settings_members() + let content = match parse_json_document(s: original.content) { + JsonDocumentParsed { value: JsonObject { members: members } } => + serialize_json(v: json_object(members: concat(filter(members, m => !any(overrides, o => o.key == m.key)), overrides))) + _ => "" + } + if content == "" || !Filesystem.Write(path: settings, content: content).success { + return exit_failure(reason: "Cannot parse or write fit-prototype process profile") + } + match with_verified_orca(appimage: appimage as FilePath, use: executable => { + let probe = run_shell_command_capture(command: orca_version_probe_command(executable: executable), transport: LocalExec) + match probe { + ProcessRefused { exit_code: _, stderr: _ } => probe + _ => run_shell_command_capture(command: orca_slice_to_project_command( + executable: executable, input_model: model, output_project: output, + printer_profile: machine, filament_profile: filament, process_profile: settings, + ), transport: LocalExec) + } + }) { + ProcessRefused { exit_code: _, stderr: e } => exit_failure(reason: e) + _ => ExitSuccess + } +} diff --git a/dag/gunbc/product/printed_chassis/slicing_toolchain.dag b/dag/gunbc/product/printed_chassis/slicing_toolchain.dag index b6cd99d7c23..0d5bb58ea31 100644 --- a/dag/gunbc/product/printed_chassis/slicing_toolchain.dag +++ b/dag/gunbc/product/printed_chassis/slicing_toolchain.dag @@ -12,7 +12,10 @@ import extdeps.printing.orca_slicer { orca_slice_to_project_command, OrcaExecutable, orca_version_probe_command, } -// WHERE THE MESH-TO-MACHINE ROUTE STOPS TODAY, DECLARED RATHER THAN DISCOVERED AT RUN TIME. +// Direct-host ABI and execution evidence. Historical refusal observations below retain their +// original hosts/environments. On 2026-10-03 slice_run executed the pinned archive successfully +// after the system packages supplied its runtime closure; with_verified_orca owns the private +// archive lifetime and prevents a caller's mutable input path becoming the executable authority. // // product.printed_chassis.export_profile ends at an STL. A Bambu Lab A1 mini in LAN Mode consumes a // sliced project, so exactly one step stands between a verified mesh and a printable artifact, and