feat: publish Dever core with project scaffolding and versioned AI skills

This commit is contained in:
Antigravity committed 2026-10-04 13:37:30 +00:00
1 parent 70fa73cdc2
commit ac458ffa7f
960 files changed
+221319 -1163

No files matched your search

@@ -0,0 +1,567 @@
//! Explicit Linux acceptance using four separately built optimized archives.
use std::fs;
use std::os::unix::fs::{OpenOptionsExt, PermissionsExt};
use std::path::{Path, PathBuf};
use std::process::{Command, Output, Stdio};
use std::time::Duration;
use dever_cli::toolchain::{Layout, MachineManager, Version, cache_status, packaging, sha256_file};
use ring::rand::SystemRandom;
use ring::signature::{Ed25519KeyPair, KeyPair};
use serde::Deserialize;
use serde_json::{Value, json};
#[path = "../support/daemon.rs"]
mod daemon;
#[path = "../support/llvm_inputs.rs"]
mod inputs;
#[path = "../../dever-tests/tests/support/process.rs"]
mod process;
#[path = "../../dever-tests/tests/support/sandbox.rs"]
mod sandbox;
use super::temp::TemporaryDirectory;
#[path = "ecosystem_acceptance.rs"]
mod ecosystems;
#[path = "bootstrap_acceptance.rs"]
mod bootstrap;
#[path = "cross_acceptance.rs"]
mod cross;
#[path = "command_acceptance.rs"]
mod command_acceptance;
const PROFILES: [&str; 4] = ["base", "sqlite", "postgres", "both"];
#[derive(Deserialize)]
#[serde(deny_unknown_fields)]
struct AcceptanceSettings {
machine_temporary_root: Option<PathBuf>,
}
fn machine_temporary_root(workspace: &Path) -> Result<Option<PathBuf>, String> {
let path = workspace.join("target/native-release-inputs/config/setting.json");
let bytes = match fs::read(&path) {
Ok(bytes) => bytes,
Err(error) if error.kind() == std::io::ErrorKind::NotFound => return Ok(None),
Err(error) => return Err(format!("cannot read acceptance settings: {error}")),
};
let settings: AcceptanceSettings = serde_json::from_slice(&bytes)
.map_err(|error| format!("invalid acceptance settings: {error}"))?;
if let Some(root) = &settings.machine_temporary_root
&& (!root.is_absolute()
|| root
.components()
.any(|part| matches!(part, std::path::Component::ParentDir)))
{
return Err("machine_temporary_root must be an absolute path without '..'".into());
}
Ok(settings.machine_temporary_root)
}
fn machine_temporary_directory(workspace: &Path) -> TemporaryDirectory {
match machine_temporary_root(workspace).expect("valid native release acceptance settings") {
Some(root) => TemporaryDirectory::new_in(&root),
None => TemporaryDirectory::new(),
}
}
#[test]
fn acceptance_machine_directory_uses_only_explicit_author_settings() {
let workspace = TemporaryDirectory::new();
assert_eq!(machine_temporary_root(workspace.path()).unwrap(), None);
let config = "target/native-release-inputs/config/setting.json";
write(
workspace.path(),
config,
br#"{"machine_temporary_root":"relative"}"#,
);
assert!(
machine_temporary_root(workspace.path())
.unwrap_err()
.contains("absolute path")
);
write(
workspace.path(),
config,
br#"{"machine_temporary_root":"/tmp","unknown":true}"#,
);
assert!(
machine_temporary_root(workspace.path())
.unwrap_err()
.contains("unknown field")
);
let selected = TemporaryDirectory::new();
write(
workspace.path(),
config,
serde_json::to_vec(&json!({"machine_temporary_root":selected.path()})).unwrap(),
);
let directory = machine_temporary_directory(workspace.path());
assert_eq!(directory.path().parent(), Some(selected.path()));
assert!(directory.path().is_dir());
let path = directory.path().to_owned();
drop(directory);
assert!(!path.exists());
assert!(selected.path().is_dir());
}
fn write(root: &Path, relative: &str, bytes: impl AsRef<[u8]>) {
let path = root.join(relative);
fs::create_dir_all(path.parent().unwrap()).unwrap();
fs::write(path, bytes).unwrap();
}
fn input(author: &Path, source: &Path, name: &str) -> Value {
let relative = format!("inputs/{name}");
let path = author.join(&relative);
fs::create_dir_all(path.parent().unwrap()).unwrap();
// This is immutable test input staging, never a published release artifact.
fs::hard_link(source, &path).unwrap();
json!({"source":relative,"sha256":sha256_file(&path).unwrap()})
}
fn author_inputs(author: &Path, workspace: &Path) -> Ed25519KeyPair {
let document = Ed25519KeyPair::generate_pkcs8(&SystemRandom::new()).unwrap();
let key = Ed25519KeyPair::from_pkcs8(document.as_ref()).unwrap();
use std::io::Write;
fs::OpenOptions::new()
.write(true)
.create_new(true)
.mode(0o600)
.open(author.join("signing.pk8"))
.unwrap()
.write_all(document.as_ref())
.unwrap();
let mut profiles = serde_json::Map::new();
for profile in PROFILES {
let name = format!("{profile}.a");
profiles.insert(
profile.into(),
input(
author,
&workspace.join("target/native-release-inputs").join(&name),
&name,
),
);
}
let compiler = input(author, Path::new(env!("CARGO_BIN_EXE_dever")), "dever");
let core_libraries = inputs::core_libraries()
.into_iter()
.map(|(name, source)| {
let mut value = input(author, &source, name);
value["path"] = json!(name);
value
})
.collect::<Vec<_>>();
let mut links = serde_json::Map::new();
for (name, source) in inputs::native_inputs()
.into_iter()
.filter(|(name, _)| *name != "runtime.a")
{
let mut entry = input(author, &source, name);
entry["path"] = json!(name);
links.insert(name.into(), entry);
}
let ordered = |names: &[&str]| {
names
.iter()
.map(|name| links[*name].clone())
.collect::<Vec<_>>()
};
let sandbox = sandbox::assets(workspace).into_iter().map(|asset| {
let relative = format!("inputs/{}", asset.path);
write(author, &relative, &asset.bytes);
fs::set_permissions(author.join(&relative), fs::Permissions::from_mode(if asset.executable { 0o755 } else { 0o644 })).unwrap();
json!({"source":relative, "path":asset.path.strip_prefix("sandbox/").unwrap(), "sha256":sha256_file(&author.join(&relative)).unwrap()})
}).collect::<Vec<_>>();
write(
author,
"inputs/SKILL.md",
b"---\nname: dever-language\ndescription: Dever acceptance skill.\n---\n",
);
let skill = json!({"source":"inputs/SKILL.md", "path":"SKILL.md", "sha256":sha256_file(&author.join("inputs/SKILL.md")).unwrap()});
write(
author,
"config/setting.json",
serde_json::to_vec(&json!({
"format":"dever-native-release-input-v1", "version":env!("CARGO_PKG_VERSION"),
"target":"x86_64-unknown-linux-gnu", "signing_key":"signing.pk8",
"core":compiler, "core_libraries":core_libraries, "profiles":profiles,
"skill":[skill],
"start":ordered(&["crt1.o","crti.o","crtbeginT.o"]),
"libraries":ordered(&["libc.a","libm.a","libmvec.a","libgcc.a","libgcc_eh.a"]),
"end":ordered(&["crtend.o","crtn.o"]), "sandbox":sandbox
}))
.unwrap(),
);
key
}
fn command(command: &mut Command) -> Output {
command_with_timeout(command, Duration::from_secs(190))
}
fn isolated_os_command(root: &Path) -> Command {
let mut command = isolated_os_namespace(root);
command.args(["--", "/fixture-proc-init"]);
command
}
fn isolated_os_namespace(root: &Path) -> Command {
let assets = Path::new(env!("CARGO_MANIFEST_DIR"))
.join("../../target/sandbox-inputs/assets")
.canonicalize()
.expect("prepare the explicit Linux isolation assets");
let init = Path::new(env!("CARGO_MANIFEST_DIR"))
.join("../../target/debug/examples/native-acceptance-init")
.canonicalize()
.expect("build the explicit static native-acceptance-init example");
let bytes = fs::read(&init).unwrap();
let executable = goblin::elf::Elf::parse(&bytes).unwrap();
assert!(executable.interpreter.is_none() && executable.libraries.is_empty());
// A plain chroot cannot create a nested user namespace. Use a real mount
// namespace root so the program can retain its normal Worker sandbox.
dever_sandbox::validate_bwrap(&fs::read(assets.join("bin/bwrap")).unwrap()).unwrap();
let mut command = Command::new(assets.join("bin/bwrap"));
command
.env_clear()
.args([
"--unshare-all",
"--die-with-parent",
"--new-session",
"--bind",
])
.arg(root)
.args(["/", "--proc", "/proc", "--dev", "/dev", "--chdir", "/"])
.arg("--ro-bind")
.arg(init)
.arg("/fixture-proc-init");
command
}
fn command_with_timeout(command: &mut Command, timeout: Duration) -> Output {
let capture = TemporaryDirectory::new();
let stdout = capture.path().join("stdout");
let stderr = capture.path().join("stderr");
let status = process::status(
command
.env_clear()
.stdout(Stdio::from(fs::File::create(&stdout).unwrap()))
.stderr(Stdio::from(fs::File::create(&stderr).unwrap())),
timeout,
)
.unwrap();
let output = Output {
status,
stdout: fs::read(stdout).unwrap(),
stderr: fs::read(stderr).unwrap(),
};
assert!(
output.status.success(),
"{:?}: {}\n{}",
command,
String::from_utf8_lossy(&output.stdout),
String::from_utf8_lossy(&output.stderr)
);
output
}
fn application(root: &Path, profile: &str) {
let sqlite = json!({"type":"sqlite","path":"data/owned.sqlite","max_connections":1});
let postgres =
json!({"type":"postgres","url":"postgres://fixture@127.0.0.1:1/unused", "tls":"disabled"});
let settings = match profile {
"base" => json!({}),
"sqlite" => json!({"database":{"default":sqlite}}),
"postgres" => json!({"database":{"default":postgres}}),
"both" => json!({"database":{"default":sqlite,"unused":postgres}}),
_ => unreachable!(),
};
write(
root,
"config/setting.json",
serde_json::to_vec(&settings).unwrap(),
);
write(
root,
"module/sample/value/api.dever",
"cmd exercise = app.exercise\n",
);
let app = if matches!(profile, "sqlite" | "both") {
write(
root,
"module/sample/value/model.dever",
"type Value { name: Text(1, 32) }\n",
);
"exercise() (okay: Bool) { saved = model.create({ name = \"pack\" })\n count = model.count()\n removed = model.delete(saved.id)\n okay = count == 1 and removed == 1 }\n"
} else {
"exercise() (okay: Bool) { dever.task.sleep(1)\n okay = true }\n"
};
write(root, "module/sample/value/app.dever", app);
}
fn assert_response(output: Output) {
assert_response_data(output, json!(true));
}
fn assert_response_data(output: Output, expected: Value) {
let value: Value = serde_json::from_slice(&output.stdout).unwrap();
assert_eq!(value["code"], 0);
assert_eq!(value["data"], expected);
}
fn exec_application(root: &Path, workspace: &Path) {
for (name, source) in [
("app", "read() (value: Int) { value = port.read() }"),
("port", "read() (value: Int) fails dever.time.SleepResult"),
(
"adapter",
"setting { mode: Text }\nexternal exec \"worker\" {}",
),
("api", "cmd read = app.read"),
] {
write(
root,
&format!("module/notification/delivery/{name}.dever"),
source,
);
}
write(
root,
"config/setting.json",
br#"{"adapter":{"notification.delivery":{"setting":{"mode":"embedded"}}}}"#,
);
let fixture = workspace.join("target/native-release-inputs/component-fixture");
assert!(
fixture.is_file(),
"prepare the immutable component-fixture in target/native-release-inputs"
);
fs::copy(fixture, root.join("module/notification/delivery/worker")).unwrap();
}
fn install_release(layout: &Layout, version: &Version, key: &Ed25519KeyPair) {
let public_key = key
.public_key()
.as_ref()
.iter()
.map(|byte| format!("{byte:02x}"))
.collect::<String>();
fs::write(layout.state().join("trusted-release-key"), public_key).unwrap();
fs::set_permissions(
layout.state().join("trusted-release-key"),
fs::Permissions::from_mode(0o644),
)
.unwrap();
MachineManager::new(layout.clone())
.install_requested(version.as_str())
.unwrap();
}
fn archive_report(workspace: &Path) -> Value {
let mut profiles = serde_json::Map::new();
for profile in PROFILES {
let path = workspace
.join("target/native-release-inputs")
.join(format!("{profile}.a"));
let output = command(
Command::new("/usr/bin/nm")
.args(["--defined-only", "--demangle"])
.arg(&path),
);
let symbols = String::from_utf8(output.stdout).unwrap();
assert_eq!(
symbols.contains(" sqlite3_open"),
matches!(profile, "sqlite" | "both"),
"{profile} SQLite driver"
);
assert_eq!(
symbols.contains("tokio_postgres"),
matches!(profile, "postgres" | "both"),
"{profile} PostgreSQL driver"
);
assert!(!symbols.contains("LLVMContextCreate"));
profiles.insert(profile.into(), json!({"bytes":fs::metadata(&path).unwrap().len(),"sha256":sha256_file(&path).unwrap()}));
}
json!(profiles)
}
#[test]
#[ignore = "requires prepared optimized archives and root for an owned GNU ABI root"]
fn signed_core_builds_without_host_compiler_libraries_or_tools() {
assert_eq!(
rustix::process::geteuid().as_raw(),
0,
"explicit owned namespace acceptance requires root"
);
let workspace = Path::new(env!("CARGO_MANIFEST_DIR")).join("../..");
let author = TemporaryDirectory::new();
author_inputs(author.path(), &workspace);
let release = machine_temporary_directory(&workspace);
let payload = release.path().join("release");
let manifest = packaging::create(author.path(), &payload).unwrap();
let isolated = machine_temporary_directory(&workspace);
ecosystems::minimal_os(isolated.path());
let compiler_root = isolated.path().join("compiler");
for artifact in &manifest.artifacts {
let relative = if artifact.path == "dever-core" {
"dever"
} else {
&artifact.path
};
let destination = compiler_root.join(relative);
fs::create_dir_all(destination.parent().unwrap()).unwrap();
// Immutable signed inputs; no host directory is mounted into the root.
fs::hard_link(payload.join(&artifact.path), destination).unwrap();
}
application(&isolated.path().join("project"), "sqlite");
let invoke = |arguments: &[&str]| command(isolated_os_command(isolated.path()).args(arguments));
invoke(&["/compiler/dever", "check", "/project"]);
invoke(&[
"/compiler/dever",
"build",
"/project",
"--output",
"/project/program",
]);
fs::remove_dir_all(isolated.path().join("project/module")).unwrap();
assert_response(invoke(&["/project/program", "sample.value.exercise", "{}"]));
write(&workspace, "target/closure-core-loader-acceptance.json", serde_json::to_vec_pretty(&json!({
"platform":"linux-x86_64", "os_baseline":"GNU libc 2.39 and its named companion libraries",
"core_libraries":manifest.artifacts.iter().filter(|artifact| artifact.path.starts_with("lib/")).collect::<Vec<_>>(),
"checks":{"signed_payload":true,"check_in_minimal_root":true,"sqlite_build_in_minimal_root":true,"source_removed_execution":true},
"host_compiler_libraries":false,"host_language_tools":false
})).unwrap());
}
#[test]
#[ignore = "requires four optimized archives and component-fixture; starts only an owned daemon, SQLite and exec Worker fixtures"]
fn optimized_profiles_make_reproducible_signed_release_and_run_through_daemon() {
let workspace = Path::new(env!("CARGO_MANIFEST_DIR")).join("../..");
let profiles = archive_report(&workspace);
let author = TemporaryDirectory::new();
let key = author_inputs(author.path(), &workspace);
let machine = machine_temporary_directory(&workspace);
let layout = Layout::new(machine.path());
layout.initialize().unwrap();
let first = machine.path().join("first");
let expected = packaging::create(author.path(), &first).unwrap();
let manifest = fs::read(first.join("manifest.json")).unwrap();
let signature = fs::read(first.join("manifest.sig")).unwrap();
// Remove only this test's first completed copy before making the second.
fs::remove_dir_all(&first).unwrap();
let version = Version::parse(env!("CARGO_PKG_VERSION")).unwrap();
let release = layout.downloads().join(version.as_str());
command(
Command::new(workspace.join("target/debug/examples/native-release"))
.arg(author.path())
.arg("--output")
.arg(&release),
);
assert_eq!(
serde_json::from_slice::<dever_cli::toolchain::ReleaseManifest>(
&fs::read(release.join("manifest.json")).unwrap()
)
.unwrap(),
expected
);
assert_eq!(fs::read(release.join("manifest.json")).unwrap(), manifest);
assert_eq!(fs::read(release.join("manifest.sig")).unwrap(), signature);
install_release(&layout, &version, &key);
fs::remove_dir_all(&release).unwrap();
drop(author);
let compiler = layout.versions().join(version.as_str()).join("dever-core");
let mut daemon = daemon::TestDaemon::start(&layout);
let projects = TemporaryDirectory::new();
let mut binaries = serde_json::Map::new();
let mut programs: Vec<(PathBuf, PathBuf)> = Vec::new();
for profile in PROFILES {
let project = projects.path().join(profile);
application(&project, profile);
command(Command::new(&compiler).arg("check").arg(&project));
assert_response(command(
Command::new(&compiler).arg("run").arg(&project).args([
"--",
"sample.value.exercise",
"{}",
]),
));
let executable = project.join("program");
command(
Command::new(&compiler)
.arg("build")
.arg(&project)
.arg("--output")
.arg(&executable),
);
binaries.insert(profile.into(), json!({"bytes":fs::metadata(&executable).unwrap().len(),"sha256":sha256_file(&executable).unwrap()}));
programs.push((project, executable));
}
assert_eq!(cache_status(&layout).unwrap().entries, 4);
let exec_project = TemporaryDirectory::new();
let exec_source = exec_project.path().to_owned();
exec_application(&exec_source, &workspace);
assert_response_data(
command(Command::new(&compiler).arg("run").arg(&exec_source).args([
"--",
"notification.delivery.read",
"{}",
])),
json!(7),
);
let standalone = TemporaryDirectory::new();
let exec_program = standalone.path().join("application");
fs::create_dir(standalone.path().join("config")).unwrap();
fs::copy(
exec_source.join("config/setting.json"),
standalone.path().join("config/setting.json"),
)
.unwrap();
command(
Command::new(&compiler)
.arg("build")
.arg(&exec_source)
.arg("--output")
.arg(&exec_program),
);
assert_eq!(cache_status(&layout).unwrap().entries, 5);
drop(exec_project);
assert!(!exec_source.exists());
daemon.stop();
assert_response_data(
command(
Command::new(&exec_program)
.current_dir(standalone.path())
.args(["notification.delivery.read", "{}"]),
),
json!(7),
);
assert!(!standalone.path().join("module").exists());
let exec = json!({
"bytes": fs::metadata(&exec_program).unwrap().len(),
"sha256": sha256_file(&exec_program).unwrap(),
"managed_run": true, "standalone_run": true,
"source_project_removed": true, "response": 7
});
for (project, executable) in programs {
fs::remove_dir_all(project.join("module")).unwrap();
assert_response(command(
Command::new(executable).args(["sample.value.exercise", "{}"]),
));
}
let report = json!({
"format":"dever-native-release-acceptance-v1", "platform":"linux-x86_64",
"version":version.as_str(), "profiles":profiles,"programs":binaries,"exec":exec,
"manifest_sha256":format!("{:x}", sha2::Sha256::digest(&manifest)),
"checks":{"pack_determinism":true,"signed_install":true,"managed_profiles":4,"standalone_profiles":4,"sqlite_crud":true,"signed_exec_worker":true},
"postgres_server":"not_run", "cross_host_source_reproducibility":"not_run"
});
use sha2::Digest;
write(
&workspace,
"target/closure-native-release-acceptance.json",
serde_json::to_vec_pretty(&report).unwrap(),
);
}
@@ -0,0 +1,410 @@
//! Bootstrap package validation and recovery use only owned fixture directories.
use super::*;
use dever_cli::toolchain::bootstrap;
use sha2::{Digest, Sha256};
#[test]
fn bootstrap_service_recovery_orders_actions_around_file_restoration() {
use bootstrap::RecoveryAction::{Reload, Restart, Stop};
for active in [false, true] {
for loaded in [false, true] {
assert_eq!(
bootstrap::recovery_actions(false, active, loaded),
(vec![], vec![])
);
}
}
assert_eq!(
bootstrap::recovery_actions(true, false, false),
(vec![], vec![Reload])
);
assert_eq!(
bootstrap::recovery_actions(true, false, true),
(vec![Stop], vec![Reload])
);
assert_eq!(
bootstrap::recovery_actions(true, true, true),
(vec![Stop], vec![Reload, Restart])
);
assert_eq!(
bootstrap::recovery_actions(true, true, false),
(vec![], vec![Reload, Restart])
);
}
fn add_bootstrap(author: &mut Author) {
let input = |name: &str, bytes: &[u8]| {
let relative = format!("inputs/{name}");
fs::write(author.root.join(&relative), bytes).unwrap();
json!({"source":relative,"sha256":sha256_file(&author.root.join(&relative)).unwrap()})
};
let launcher = input(
"dever",
&native_elf::compiler(&["libgcc_s.so.1", "libc.so.6"]),
);
let daemon = input(
"deverd",
&native_elf::compiler(&["libgcc_s.so.1", "libc.so.6"]),
);
let mut gcc = input("gcc", &native_elf::library("libgcc_s.so.1", &["libc.so.6"]));
gcc["path"] = json!("libgcc_s.so.1");
author.settings["bootstrap"] = json!({"launcher":launcher,"daemon":daemon,"libraries":[gcc]});
}
#[test]
fn bootstrap_payload_has_a_separate_signed_complete_loader_closure() {
let mut author = Author::new();
add_bootstrap(&mut author);
author.save();
let release = author.output("complete");
let manifest = packaging::create(&author.root, &release).unwrap();
assert_signed(&author, &release);
for path in [
"bootstrap/dever",
"bootstrap/deverd",
"bootstrap/lib/libgcc_s.so.1",
"bootstrap/deverd.service",
"bootstrap/dever-sandbox",
] {
let artifact = manifest
.artifacts
.iter()
.find(|artifact| artifact.path == path)
.unwrap();
assert_eq!(artifact.sha256, sha256_file(&release.join(path)).unwrap());
}
let unit = fs::read_to_string(release.join("bootstrap/deverd.service")).unwrap();
assert!(unit.contains("ExecStart=/opt/dever/bin/deverd --root /opt/dever"));
assert!(unit.contains("RuntimeDirectory=dever"));
assert!(!unit.contains("Environment"));
assert!(!unit.contains("PATH"));
assert_eq!(
fs::read_to_string(release.join("bootstrap/dever-sandbox")).unwrap(),
dever_sandbox::APPARMOR_PROFILE
);
}
#[test]
fn bootstrap_authoring_rejects_missing_libraries_wrong_sonames_and_search_paths() {
let mut author = Author::new();
add_bootstrap(&mut author);
author.settings["bootstrap"]["libraries"] = json!([]);
assert!(author.reject().contains("libgcc_s.so.1"));
let mut author = Author::new();
add_bootstrap(&mut author);
fs::write(
author.root.join("inputs/gcc"),
native_elf::library("libwrong.so", &["libc.so.6"]),
)
.unwrap();
author.settings["bootstrap"]["libraries"][0]["sha256"] =
json!(sha256_file(&author.root.join("inputs/gcc")).unwrap());
assert!(author.reject().contains("SONAME"));
let mut author = Author::new();
add_bootstrap(&mut author);
fs::write(
author.root.join("inputs/dever"),
native_elf::elf(None, &["libgcc_s.so.1"], Some("/usr/lib")),
)
.unwrap();
author.settings["bootstrap"]["launcher"]["sha256"] =
json!(sha256_file(&author.root.join("inputs/dever")).unwrap());
assert!(author.reject().contains("RPATH"));
}
#[test]
fn signed_installed_versions_keep_their_original_bootstrap_service_template() {
use dever_cli::toolchain::{Layout, MachineManager, Version};
let mut author = Author::new();
add_bootstrap(&mut author);
author.save();
let machine = TemporaryDirectory::new();
let layout = Layout::new(machine.path().join("machine"));
layout.initialize().unwrap();
let version = Version::parse(env!("CARGO_PKG_VERSION")).unwrap();
let installed = layout.versions().join(version.as_str());
let mut manifest = packaging::create(&author.root, &installed).unwrap();
let service = installed.join("bootstrap/deverd.service");
let current = fs::read_to_string(&service).unwrap();
let old = current.replace("RestartSec=1", "RestartSec=2");
assert_ne!(current, old);
fs::write(&service, old).unwrap();
let artifact = manifest
.artifacts
.iter_mut()
.find(|artifact| artifact.path == "bootstrap/deverd.service")
.unwrap();
artifact.bytes = fs::metadata(&service).unwrap().len();
artifact.sha256 = sha256_file(&service).unwrap();
let encoded = serde_json::to_vec(&manifest).unwrap();
let key =
Ed25519KeyPair::from_pkcs8(&fs::read(author.root.join("signing.pk8")).unwrap()).unwrap();
fs::write(installed.join("manifest.json"), &encoded).unwrap();
fs::write(
installed.join("manifest.sig"),
key.sign(&encoded)
.as_ref()
.iter()
.map(|byte| format!("{byte:02x}"))
.collect::<String>(),
)
.unwrap();
fs::write(
layout.state().join("trusted-release-key"),
author
.public_key
.iter()
.map(|byte| format!("{byte:02x}"))
.collect::<String>(),
)
.unwrap();
let manager = MachineManager::new(layout);
assert_eq!(
manager.resolve_core(&version).unwrap(),
installed.join("dever-core")
);
fs::write(service, b"unsigned modification").unwrap();
assert!(
manager
.resolve_core(&version)
.unwrap_err()
.contains("integrity verification")
);
}
#[test]
#[ignore = "root-only installer trust rejection; never touches the actual machine installation"]
fn bootstrap_installer_rejects_untrusted_inputs_before_publication() {
assert_eq!(rustix::process::geteuid().as_raw(), 0);
assert!(
bootstrap::install(Path::new("."))
.unwrap_err()
.contains("absolute paths")
);
assert!(
bootstrap::recover(Path::new("."))
.unwrap_err()
.contains("absolute paths")
);
let mut author = Author::new();
add_bootstrap(&mut author);
author.save();
let package = author.output("release");
packaging::create(&author.root, &package).unwrap();
let installer = TemporaryDirectory::new();
let system = TemporaryDirectory::new();
let key = installer.path().join("trusted.pub");
fs::write(
&key,
author
.public_key
.iter()
.map(|byte| format!("{byte:02x}"))
.collect::<String>(),
)
.unwrap();
fs::create_dir(installer.path().join("config")).unwrap();
let save = |key: &Path, activate: bool| {
fs::write(installer.path().join("config/setting.json"), serde_json::to_vec(&json!({"release":package,"system_root":system.path(),"trusted_key":key,"activate_service":activate})).unwrap()).unwrap()
};
save(&key, true);
assert!(
bootstrap::install(installer.path())
.unwrap_err()
.contains("actual system root")
);
let inside = package.join("trusted.pub");
fs::copy(&key, &inside).unwrap();
save(&inside, false);
assert!(
bootstrap::install(installer.path())
.unwrap_err()
.contains("independently")
);
save(&key, false);
fs::set_permissions(&key, fs::Permissions::from_mode(0o666)).unwrap();
assert!(
bootstrap::install(installer.path())
.unwrap_err()
.contains("root-owned")
);
fs::set_permissions(&key, fs::Permissions::from_mode(0o644)).unwrap();
let signature = fs::read(package.join("manifest.sig")).unwrap();
fs::write(package.join("manifest.sig"), "00".repeat(64)).unwrap();
assert!(
bootstrap::install(installer.path())
.unwrap_err()
.contains("signature verification")
);
fs::write(package.join("manifest.sig"), signature).unwrap();
// The key is pinned before the intentionally non-executable synthetic core
// fails its health check. Empty/partial temporary files model interrupted
// writes without injecting product failpoints or touching real services.
let state = system.path().join("opt/dever/state");
let temporary_key = state.join(".trusted-release-key.next");
let pinned_key = state.join("trusted-release-key");
for partial in ["", "abc"] {
fs::write(&temporary_key, partial).unwrap();
let error = bootstrap::install(installer.path()).unwrap_err();
assert!(!error.contains("temporary release key"), "{error}");
assert_eq!(
fs::read_to_string(&pinned_key).unwrap().trim(),
fs::read_to_string(&key).unwrap()
);
assert!(!temporary_key.exists());
fs::remove_file(&pinned_key).unwrap();
}
fs::write(&pinned_key, "11".repeat(32)).unwrap();
assert!(
bootstrap::install(installer.path())
.unwrap_err()
.contains("pinned release key")
);
assert_eq!(fs::read_to_string(&pinned_key).unwrap(), "11".repeat(32));
fs::remove_file(&pinned_key).unwrap();
fs::write(package.join("bootstrap/lib/libgcc_s.so.1"), b"tampered").unwrap();
assert!(
bootstrap::install(installer.path())
.unwrap_err()
.contains("integrity verification")
);
fs::remove_file(package.join("bootstrap/lib/libgcc_s.so.1")).unwrap();
assert!(bootstrap::install(installer.path()).is_err());
assert!(!system.path().join("usr/local/bin/dever").exists());
assert!(
!system
.path()
.join("opt/dever/state/active-version")
.exists()
);
}
// Independent construction of a one-node journal identity lets recovery tests
// simulate interruption at publication boundaries without product failpoints.
fn node_hash(kind: &str, bytes: &[u8]) -> String {
let mut hash = Sha256::new();
hash.update(0u64.to_le_bytes());
hash.update(
if kind == "directory" {
0o755u32
} else {
0o644u32
}
.to_le_bytes(),
);
hash.update(kind.as_bytes());
if kind == "file" {
hash.update((bytes.len() as u64).to_le_bytes());
hash.update(bytes);
}
format!("{:x}", hash.finalize())
}
#[test]
#[ignore = "root-only crash-recovery fixture; all journal targets are below an owned image root"]
fn bootstrap_recovery_is_idempotent_and_rejects_unrecorded_targets() {
assert_eq!(rustix::process::geteuid().as_raw(), 0);
let system = TemporaryDirectory::new();
let root = system.path();
let layout = dever_cli::toolchain::Layout::new(root.join("opt/dever"));
layout.initialize().unwrap();
for directory in [
"usr/local/bin",
"etc/systemd/system/multi-user.target.wants",
"etc/apparmor.d",
] {
fs::create_dir_all(root.join(directory)).unwrap();
}
let service = root.join("etc/systemd/system/deverd.service");
fs::write(&service, b"new service").unwrap();
let entry = root.join("usr/local/bin/dever");
symlink("/opt/dever/bin/dever", &entry).unwrap();
let enable_stage = root
.join("etc/systemd/system/multi-user.target.wants/.dever-bootstrap-deverd.service.next");
symlink("../deverd.service", &enable_stage).unwrap();
let active_stage = layout.state().join(".dever-bootstrap-active-version.next");
fs::write(&active_stage, b"new active").unwrap();
let sentinel = root.join("etc/systemd/system/unrelated.service");
fs::write(&sentinel, b"keep").unwrap();
let link_hash =
|target: &str| format!("{:x}", Sha256::digest(format!("link\0{target}").as_bytes()));
let journal = json!({"format":"dever-bootstrap-transaction-v1","phase":"prepared","service_was_active":null,"service_start_requested":false,"changes":[
{"target":"sandbox","before":null,"after":node_hash("directory",b"")},
{"target":"apparmor","before":null,"after":node_hash("file",b"new profile")},
{"target":"bin","before":node_hash("directory",b""),"after":node_hash("directory",b"")},
{"target":"service","before":null,"after":node_hash("file",b"new service")},
{"target":"entry","before":null,"after":link_hash("/opt/dever/bin/dever")},
{"target":"enable","before":null,"after":link_hash("../deverd.service")},
{"target":"active","before":null,"after":node_hash("file",b"new active")}
]});
fs::create_dir(root.join("opt/dever/.dever-bootstrap-sandbox.next")).unwrap();
fs::write(
root.join("etc/apparmor.d/.dever-bootstrap-dever-sandbox.next"),
b"new profile",
)
.unwrap();
let path = layout.state().join("bootstrap-install.json");
fs::write(&path, serde_json::to_vec(&journal).unwrap()).unwrap();
let mut invalid = journal.clone();
invalid["changes"][0]["target"] = json!("../../unrelated");
fs::write(&path, serde_json::to_vec(&invalid).unwrap()).unwrap();
assert!(bootstrap::recover(root).is_err());
assert_eq!(fs::read(&sentinel).unwrap(), b"keep");
fs::write(&path, serde_json::to_vec(&journal).unwrap()).unwrap();
fs::write(&service, b"not this transaction").unwrap();
assert!(
bootstrap::recover(root)
.unwrap_err()
.contains("outside this transaction")
);
assert_eq!(fs::read(&service).unwrap(), b"not this transaction");
fs::write(&service, b"new service").unwrap();
bootstrap::recover(root).unwrap();
bootstrap::recover(root).unwrap();
assert!(!service.exists());
assert!(fs::symlink_metadata(&entry).is_err());
assert!(!active_stage.exists());
assert!(fs::symlink_metadata(&enable_stage).is_err());
assert_eq!(fs::read(&sentinel).unwrap(), b"keep");
assert!(!path.exists());
// A committed publication cleans only its old staging copies; it must not
// undo the new active version, including after a failed cleanup retry.
fs::write(&service, b"new service").unwrap();
symlink("/opt/dever/bin/dever", &entry).unwrap();
fs::create_dir(root.join("opt/dever/sandbox")).unwrap();
fs::write(root.join("etc/apparmor.d/dever-sandbox"), b"new profile").unwrap();
symlink(
"../deverd.service",
root.join("etc/systemd/system/multi-user.target.wants/deverd.service"),
)
.unwrap();
fs::write(layout.state().join("active-version"), b"new active").unwrap();
fs::write(&active_stage, b"old active").unwrap();
let mut committed = journal;
committed["phase"] = json!("committed");
fs::write(&path, serde_json::to_vec(&committed).unwrap()).unwrap();
fs::write(&service, b"unexpected modification").unwrap();
assert!(
bootstrap::recover(root)
.unwrap_err()
.contains("committed bootstrap destination changed")
);
assert!(path.exists());
assert_eq!(
fs::read(layout.state().join("active-version")).unwrap(),
b"new active"
);
fs::write(&service, b"new service").unwrap();
bootstrap::recover(root).unwrap();
bootstrap::recover(root).unwrap();
assert_eq!(
fs::read(layout.state().join("active-version")).unwrap(),
b"new active"
);
assert!(!active_stage.exists());
assert!(!path.exists());
assert_eq!(fs::read(sentinel).unwrap(), b"keep");
}
@@ -0,0 +1,476 @@
//! Real bootstrap installation and execution; no global service is changed.
use super::*;
use std::os::unix::fs::{MetadataExt, chown};
use std::os::unix::process::CommandExt;
use std::process::Child;
struct Daemon(Child);
impl Drop for Daemon {
fn drop(&mut self) {
let _ = self.0.kill();
let _ = self.0.wait();
}
}
fn wait_socket(daemon: &mut Daemon, socket: &Path) {
for _ in 0..200 {
assert!(
daemon.0.try_wait().unwrap().is_none(),
"installed daemon exited before becoming ready"
);
if socket.exists() {
return;
}
std::thread::sleep(Duration::from_millis(20));
}
panic!("installed daemon did not create its socket");
}
/// Clients share the daemon's PID namespace so SO_PEERCRED can identify both
/// peers. Separate bwrap invocations would hide the daemon PID from clients.
struct IsolatedDaemon {
process: Daemon,
namespace_pid: u32,
namespace: std::os::fd::OwnedFd,
root: PathBuf,
stopped: bool,
}
impl IsolatedDaemon {
fn start(root: &Path) -> Self {
let information = TemporaryDirectory::new();
let path = information.path().join("namespace.json");
let output = fs::File::create(&path).unwrap();
let mut process = Daemon(
isolated_os_namespace(root)
.args([
"--info-fd",
"1",
"--",
"/fixture-proc-init",
"/opt/dever/bin/deverd",
"--root",
"/opt/dever",
])
.stdout(Stdio::from(output))
.stderr(Stdio::inherit())
.spawn()
.unwrap(),
);
wait_socket(&mut process, &root.join("run/dever/deverd.sock"));
assert!(fs::metadata(&path).unwrap().len() <= 4096);
let info: Value = serde_json::from_slice(&fs::read(path).unwrap()).unwrap();
let namespace_pid = u32::try_from(info["child-pid"].as_u64().unwrap()).unwrap();
assert_ne!(namespace_pid, 0);
let namespace = rustix::process::pidfd_open(
rustix::process::Pid::from_raw(i32::try_from(namespace_pid).unwrap()).unwrap(),
rustix::process::PidfdFlags::empty(),
)
.unwrap();
Self {
process,
namespace_pid,
namespace,
root: root.to_owned(),
stopped: false,
}
}
fn client(&mut self) -> Command {
assert!(self.process.0.try_wait().unwrap().is_none());
let target = fs::metadata(format!("/proc/{}/root", self.namespace_pid)).unwrap();
let expected = fs::metadata(&self.root).unwrap();
assert_eq!(
(target.dev(), target.ino()),
(expected.dev(), expected.ino())
);
let mut command = Command::new("/usr/bin/nsenter");
command.env_clear().args([
"--target",
&self.namespace_pid.to_string(),
"--all",
"--root",
"--wdns=/",
"--",
]);
command
}
fn stop(&mut self) -> Result<(), String> {
if self.stopped {
return Ok(());
}
// Target the pinned namespace init, not a reusable numeric PID. Its
// termination also kills its namespace descendants; waiting only for
// the outer bwrap process does not establish daemon lock release.
match rustix::process::pidfd_send_signal(&self.namespace, rustix::process::Signal::KILL) {
Ok(()) | Err(rustix::io::Errno::SRCH) => {}
Err(error) => return Err(format!("cannot stop owned namespace: {error}")),
}
let expected = fs::metadata(&self.root).map_err(|error| error.to_string())?;
let lock = fs::File::open(self.root.join("opt/dever/state/deverd.lock"))
.map_err(|error| error.to_string())?;
let deadline = std::time::Instant::now() + Duration::from_secs(10);
loop {
let wrapper_exited = self
.process
.0
.try_wait()
.map_err(|error| error.to_string())?
.is_some();
let namespace_exited = match fs::metadata(format!("/proc/{}/root", self.namespace_pid))
{
Ok(current) => (current.dev(), current.ino()) != (expected.dev(), expected.ino()),
Err(error) if error.kind() == std::io::ErrorKind::NotFound => true,
Err(error) => return Err(format!("cannot inspect owned namespace exit: {error}")),
};
if wrapper_exited && namespace_exited {
match fs2::FileExt::try_lock_exclusive(&lock) {
Ok(()) => {
self.stopped = true;
return Ok(());
}
Err(error) if error.kind() == std::io::ErrorKind::WouldBlock => {}
Err(error) => {
return Err(format!("cannot check owned daemon lock release: {error}"));
}
}
}
if std::time::Instant::now() >= deadline {
return Err("owned namespace or daemon lock remained live after 10 seconds".into());
}
std::thread::sleep(Duration::from_millis(20));
}
}
}
impl Drop for IsolatedDaemon {
fn drop(&mut self) {
// Cleanup must not cause a second panic while unwinding a failed
// assertion. Normal stage transitions explicitly check stop().
if let Err(error) = self.stop() {
eprintln!("bootstrap fixture cleanup failed: {error}");
}
}
}
fn add_inputs(author: &Path) {
let launcher = input(
author,
Path::new(env!("CARGO_BIN_EXE_dever-launcher")),
"bootstrap/dever",
);
let daemon = input(
author,
Path::new(env!("CARGO_BIN_EXE_deverd")),
"bootstrap/deverd",
);
let mut gcc = input(
author,
Path::new("/lib/x86_64-linux-gnu/libgcc_s.so.1"),
"bootstrap/libgcc_s.so.1",
);
gcc["path"] = json!("libgcc_s.so.1");
let path = author.join("config/setting.json");
let mut settings: Value = serde_json::from_slice(&fs::read(&path).unwrap()).unwrap();
settings["bootstrap"] = json!({"launcher":launcher,"daemon":daemon,"libraries":[gcc]});
fs::write(path, serde_json::to_vec(&settings).unwrap()).unwrap();
}
#[test]
#[ignore = "requires optimized author archives and Linux root; installs only into an owned image root"]
fn signed_bootstrap_installs_and_serves_two_projects_without_host_libraries() {
assert_eq!(rustix::process::geteuid().as_raw(), 0);
let workspace = Path::new(env!("CARGO_MANIFEST_DIR")).join("../..");
let author = TemporaryDirectory::new();
let key = author_inputs(author.path(), &workspace);
add_inputs(author.path());
let downloaded = machine_temporary_directory(&workspace);
let release = downloaded.path().join("release");
let manifest = packaging::create(author.path(), &release).unwrap();
let image = machine_temporary_directory(&workspace);
ecosystems::minimal_os(image.path());
let installer = TemporaryDirectory::new();
let public = installer.path().join("release.pub");
fs::write(
&public,
key.public_key()
.as_ref()
.iter()
.map(|byte| format!("{byte:02x}"))
.collect::<String>(),
)
.unwrap();
write(installer.path(), "config/setting.json", serde_json::to_vec(&json!({
"release":release,"system_root":image.path(),"trusted_key":public,"activate_service":false
})).unwrap());
// This author-built installer is the independently trusted first program;
// no executable from an unchecked downloaded directory starts as root.
command(
Command::new(env!("CARGO_BIN_EXE_dever-launcher"))
.arg("--dever-install")
.arg(installer.path()),
);
let layout = Layout::new(image.path().join("opt/dever"));
let sandbox_artifact = manifest
.artifacts
.iter()
.find(|artifact| artifact.path == "sandbox/linux-x86_64/bin/bwrap")
.unwrap();
let sandbox_store = layout.root().join("sandbox");
let sandbox_helper = sandbox_store.join(&sandbox_artifact.sha256).join("bwrap");
assert_eq!(
sha256_file(&sandbox_helper).unwrap(),
sandbox_artifact.sha256
);
assert_eq!(
fs::metadata(&sandbox_helper).unwrap().mode() & 0o7777,
0o755
);
assert_eq!(
fs::read_to_string(image.path().join("etc/apparmor.d/dever-sandbox")).unwrap(),
dever_sandbox::APPARMOR_PROFILE
);
let launcher = layout.bin().join("dever");
let executable = layout.bin().join("deverd");
assert_eq!(
fs::read_link(image.path().join("usr/local/bin/dever")).unwrap(),
Path::new("/opt/dever/bin/dever")
);
assert_eq!(
fs::read_link(
image
.path()
.join("etc/systemd/system/multi-user.target.wants/deverd.service")
)
.unwrap(),
Path::new("../deverd.service")
);
let unit = fs::read_to_string(image.path().join("etc/systemd/system/deverd.service")).unwrap();
assert!(unit.contains("ExecStart=/opt/dever/bin/deverd --root /opt/dever"));
assert!(unit.contains("User=root"));
for file in [
&launcher,
&executable,
&layout.bin().join("lib/libgcc_s.so.1"),
&layout.state().join("trusted-release-key"),
&image.path().join("etc/systemd/system/deverd.service"),
] {
let metadata = fs::metadata(file).unwrap();
assert_eq!(metadata.uid(), 0);
assert_eq!(metadata.mode() & 0o022, 0);
}
let active = fs::read(layout.state().join("active-version")).unwrap();
let launcher_hash = sha256_file(&launcher).unwrap();
// Model an older, correctly signed bootstrap service template. Its exact
// signed bytes establish ownership; only the incoming payload must match
// the current installer's supported service template.
let unit_path = image.path().join("etc/systemd/system/deverd.service");
let current_unit = fs::read_to_string(&unit_path).unwrap();
let old_unit = current_unit.replace("RestartSec=1", "RestartSec=2");
assert_ne!(old_unit, current_unit);
fs::write(layout.bin().join("deverd.service"), &old_unit).unwrap();
let mut old_manifest: Value =
serde_json::from_slice(&fs::read(layout.bin().join("manifest.json")).unwrap()).unwrap();
let service_artifact = old_manifest["artifacts"]
.as_array_mut()
.unwrap()
.iter_mut()
.find(|artifact| artifact["path"] == "bootstrap/deverd.service")
.unwrap();
service_artifact["bytes"] = json!(old_unit.len());
service_artifact["sha256"] = json!(sha256_file(&layout.bin().join("deverd.service")).unwrap());
let encoded = serde_json::to_vec(&old_manifest).unwrap();
fs::write(
layout.bin().join("manifest.sig"),
key.sign(&encoded)
.as_ref()
.iter()
.map(|byte| format!("{byte:02x}"))
.collect::<String>(),
)
.unwrap();
fs::write(layout.bin().join("manifest.json"), encoded).unwrap();
fs::write(&unit_path, b"unrelated system unit").unwrap();
let rejected = Command::new(env!("CARGO_BIN_EXE_dever-launcher"))
.env_clear()
.arg("--dever-install")
.arg(installer.path())
.output()
.unwrap();
assert!(!rejected.status.success());
assert!(String::from_utf8_lossy(&rejected.stderr).contains("unrelated system service"));
assert_eq!(
fs::read(layout.state().join("active-version")).unwrap(),
active
);
fs::write(&unit_path, old_unit).unwrap();
// Retain a different, correctly signed static helper identity through an
// upgrade. ELF trailing padding keeps this executable's actual behavior.
let mut previous_helper = fs::read(&sandbox_helper).unwrap();
previous_helper.push(0);
let previous_hash = dever_sandbox::validate_bwrap(&previous_helper).unwrap();
let previous_root = sandbox_store.join(&previous_hash);
fs::create_dir(&previous_root).unwrap();
fs::write(previous_root.join("bwrap"), &previous_helper).unwrap();
fs::set_permissions(
previous_root.join("bwrap"),
fs::Permissions::from_mode(0o755),
)
.unwrap();
let mut previous_manifest = manifest.clone();
let previous_artifact = previous_manifest
.artifacts
.iter_mut()
.find(|artifact| artifact.path == sandbox_artifact.path)
.unwrap();
previous_artifact.sha256 = previous_hash.clone();
previous_artifact.bytes = previous_helper.len() as u64;
let previous_encoded = serde_json::to_vec(&previous_manifest).unwrap();
fs::write(
previous_root.join("manifest.sig"),
key.sign(&previous_encoded)
.as_ref()
.iter()
.map(|byte| format!("{byte:02x}"))
.collect::<String>(),
)
.unwrap();
fs::write(previous_root.join("manifest.json"), previous_encoded).unwrap();
// Retry is supported, but a later damaged package cannot damage the
// published launcher or compiler selection.
command(
Command::new(env!("CARGO_BIN_EXE_dever-launcher"))
.arg("--dever-install")
.arg(installer.path()),
);
assert_eq!(fs::read_to_string(&unit_path).unwrap(), current_unit);
assert_eq!(
sha256_file(&sandbox_helper).unwrap(),
sandbox_artifact.sha256
);
assert_eq!(
sha256_file(&previous_root.join("bwrap")).unwrap(),
previous_hash
);
assert_eq!(fs::read_dir(&sandbox_store).unwrap().count(), 2);
let before_bad_install = fs::read(layout.state().join("active-version")).unwrap();
fs::write(
release.join("bootstrap/lib/libgcc_s.so.1"),
b"changed after signing",
)
.unwrap();
let failed = Command::new(env!("CARGO_BIN_EXE_dever-launcher"))
.env_clear()
.arg("--dever-install")
.arg(installer.path())
.output()
.unwrap();
assert!(!failed.status.success());
assert_eq!(
fs::read(layout.state().join("active-version")).unwrap(),
before_bad_install
);
assert_eq!(sha256_file(&launcher).unwrap(), launcher_hash);
assert!(!active.is_empty());
drop(downloaded);
drop(author);
drop(installer);
for name in ["first", "second"] {
application(&image.path().join(name), "base");
}
let mut daemon = IsolatedDaemon::start(image.path());
for name in ["first", "second"] {
command(
daemon
.client()
.args(["/usr/local/bin/dever", "check", &format!("/{name}")]),
);
assert_response(command(daemon.client().args([
"/usr/local/bin/dever",
"run",
&format!("/{name}"),
"--",
"sample.value.exercise",
"{}",
])));
command(daemon.client().args([
"/usr/local/bin/dever",
"build",
&format!("/{name}"),
"--output",
&format!("/{name}/program"),
]));
}
let status = command(
daemon
.client()
.args(["/usr/local/bin/dever", "cache", "status"]),
);
assert_eq!(
serde_json::from_slice::<Value>(&status.stdout).unwrap()["entries"],
1
);
daemon
.stop()
.expect("isolated daemon and namespace fully stopped");
drop(daemon);
// Namespace-root execution above proves the complete loader closure. These
// clients retain real distinct kernel UIDs, without a single-UID userns
// translating file ownership to overflow IDs.
fs::set_permissions(image.path(), fs::Permissions::from_mode(0o755)).unwrap();
let mut daemon = Daemon(
Command::new(&executable)
.env_clear()
.args(["--root", layout.root().to_str().unwrap()])
.stdout(Stdio::null())
.stderr(Stdio::inherit())
.spawn()
.unwrap(),
);
wait_socket(&mut daemon, &layout.service_socket());
for (name, uid) in [("first", 65533), ("second", 65534)] {
let project = image.path().join(name);
chown(&project, Some(uid), Some(uid)).unwrap();
assert_response(command(
Command::new(&launcher)
.uid(uid)
.gid(uid)
.arg("run")
.arg(&project)
.args(["--", "sample.value.exercise", "{}"]),
));
let denied = Command::new(&launcher)
.uid(uid)
.gid(uid)
.env_clear()
.args(["use", manifest.version.as_str()])
.output()
.unwrap();
assert!(!denied.status.success());
assert_eq!(
fs::read(layout.state().join("active-version")).unwrap(),
before_bad_install
);
}
assert_eq!(cache_status(&layout).unwrap().entries, 1);
drop(daemon);
for name in ["first", "second"] {
fs::remove_dir_all(image.path().join(name).join("module")).unwrap();
}
fs::remove_dir_all(layout.root()).unwrap();
for name in ["first", "second"] {
assert_response(command(isolated_os_command(image.path()).args([
&format!("/{name}/program"),
"sample.value.exercise",
"{}",
])));
}
write(&workspace, "target/closure-linux-bootstrap-acceptance.json", serde_json::to_vec_pretty(&json!({
"format":"dever-linux-bootstrap-acceptance-v1","platform":"linux-x86_64",
"bootstrap":manifest.artifacts.iter().filter(|artifact| artifact.path.starts_with("bootstrap/")).collect::<Vec<_>>(),
"checks":{"signed_first_install":true,"stable_public_entry":true,"root_owned_systemd_assets":true,"two_projects_in_minimal_os_root":true,"real_distinct_uids":true,"shared_compile_entries":1,"failed_install_preserves_old_entry":true,"source_and_machine_removed_execution":true},
"global_service_actions":"not_run","public_release":"not_published"
})).unwrap());
}
@@ -0,0 +1,205 @@
//! Owned signed CLI and relocated output; the tool is an ordinary dynamic ELF.
use super::*;
use dever_cli::libs::LockFile;
use dever_cli::packages::LockedPackage;
use dever_cli::toolchain::artifact_put;
use sha2::{Digest, Sha256};
use std::io::{Cursor, Write};
fn project(root: &Path, workspace: &Path) {
write(
root,
"module/media/tool/port.dever",
"execute(args: List<Text>, stdin: Bytes?) (output: dever.process.Output) fails dever.process.Error\n",
);
write(
root,
"module/media/tool/adapter.dever",
"external command \"bin/tool\" {}\n",
);
write(
root,
"module/media/tool/app.dever",
r#"execute() (passed: Bool) {
input = dever.bytes.from_ints([0, 255, 128, 10])
output = port.execute(["a b", "$(literal);*"], input)
passed = output.code == 23 and output.stdout == input and dever.bytes.to_text(output.stderr) == "ordinary command"
}
"#,
);
write(
root,
"module/media/tool/api.dever",
"cmd execute = app.execute\n",
);
write(root, "config/setting.json", "{}");
let directory = root.join("module/media/tool/bin");
fs::create_dir_all(directory.join("lib")).unwrap();
command(
Command::new("/usr/bin/cc")
.arg("-B/usr/bin/")
.args(["-shared", "-fPIC", "-Wl,-soname,libcommand.so", "-o"])
.arg(directory.join("lib/libcommand.so"))
.arg(workspace.join("test/dever-cli-tests/support/command_library.c")),
);
command(
Command::new("/usr/bin/cc")
.arg("-B/usr/bin/")
.arg(workspace.join("test/dever-cli-tests/support/command_tool.c"))
.arg("-L")
.arg(directory.join("lib"))
.arg("-lcommand")
.arg("-o")
.arg(directory.join("tool")),
);
}
fn packaged_project(root: &Path, source: &Path, layout: &Layout) {
let manifest = br#"{"format":"dever-package-v1","name":"media","version":"1.0.0","dependencies":{},"lib":[]}"#;
let mut zip = zip::ZipWriter::new(Cursor::new(Vec::new()));
zip.start_file(
"dever-package.json",
zip::write::SimpleFileOptions::default(),
)
.unwrap();
zip.write_all(manifest).unwrap();
for file in [
"port.dever",
"adapter.dever",
"app.dever",
"api.dever",
"bin/tool",
"bin/lib/libcommand.so",
] {
let name = format!("module/media/tool/{file}");
zip.start_file(&name, zip::write::SimpleFileOptions::default())
.unwrap();
zip.write_all(&fs::read(source.join(name)).unwrap())
.unwrap();
}
let archive = zip.finish().unwrap().into_inner();
let receipt = artifact_put(layout, &archive).unwrap();
fs::create_dir_all(root.join("module")).unwrap();
write(
root,
"config/setting.json",
r#"{"package":{"registry":"https://packages.example.test","use":["media@1.0.0"]}}"#,
);
let mut lock = LockFile::new(Vec::new()).unwrap();
lock.packages.push(LockedPackage {
name: "media".into(),
version: "1.0.0".into(),
sha256: receipt.sha256,
bytes: archive.len() as u64,
dependencies: Vec::new(),
manifest_sha256: format!("{:x}", Sha256::digest(manifest)),
});
lock.write_atomic(root).unwrap();
}
#[test]
#[ignore = "requires explicit current runtime archive, author C/LLVM inputs and owned daemon/sandbox"]
fn signed_cli_runs_and_packages_ordinary_command_with_private_library() {
let workspace = Path::new(env!("CARGO_MANIFEST_DIR")).join("../..");
let author = TemporaryDirectory::new();
let key = author_inputs(author.path(), &workspace);
// This test validates the command boundary, not release-profile trimming.
// Use the explicitly rebuilt archive containing the current wire ABI.
let current = input(
author.path(),
&workspace.join("target/native-runtime-abi/debug/libdever_backend_bridge.a"),
"command-base.a",
);
let settings_path = author.path().join("config/setting.json");
let mut settings: Value = serde_json::from_slice(&fs::read(&settings_path).unwrap()).unwrap();
settings["profiles"]["base"] = current;
fs::write(settings_path, serde_json::to_vec(&settings).unwrap()).unwrap();
let machine = machine_temporary_directory(&workspace);
let layout = Layout::new(machine.path());
layout.initialize().unwrap();
let version = Version::parse(env!("CARGO_PKG_VERSION")).unwrap();
let release = layout.downloads().join(version.as_str());
packaging::create(author.path(), &release).unwrap();
install_release(&layout, &version, &key);
let compiler = layout.versions().join(version.as_str()).join("dever-core");
let mut daemon = daemon::TestDaemon::start(&layout);
let source = TemporaryDirectory::new();
project(source.path(), &workspace);
command(Command::new(&compiler).arg("check").arg(source.path()));
assert_response(command(
Command::new(&compiler).arg("run").arg(source.path()).args([
"--",
"media.tool.execute",
"{}",
]),
));
assert!(!source.path().join("dever.lock").exists());
let deployment = TemporaryDirectory::new();
let executable = deployment.path().join("program");
command(
Command::new(&compiler)
.arg("build")
.arg(source.path())
.arg("--output")
.arg(&executable),
);
assert_eq!(cache_status(&layout).unwrap().entries, 1);
let package_source = TemporaryDirectory::new();
packaged_project(package_source.path(), source.path(), &layout);
assert_response(command(
Command::new(&compiler)
.arg("run")
.arg(package_source.path())
.args(["--", "media.tool.execute", "{}"]),
));
let package_executable = deployment.path().join("package-program");
command(
Command::new(&compiler)
.arg("build")
.arg(package_source.path())
.arg("--output")
.arg(&package_executable),
);
write(deployment.path(), "config/setting.json", "{}");
daemon.stop();
drop(source);
drop(package_source);
drop(author);
drop(machine);
assert_response(command(
Command::new(&executable)
.args(["media.tool.execute", "{}"])
.current_dir(deployment.path()),
));
assert_response(command(
Command::new(&package_executable)
.args(["media.tool.execute", "{}"])
.current_dir(deployment.path()),
));
let cache = deployment.path().join("data/cache/lib");
let bundle = fs::read_dir(cache).unwrap().next().unwrap().unwrap().path();
let commands = bundle.join("commands");
let tree = fs::read_dir(commands)
.unwrap()
.next()
.unwrap()
.unwrap()
.path();
assert!(tree.join("lib/libcommand.so").is_file());
fs::write(tree.join("lib/libcommand.so"), b"tampered").unwrap();
let capture = deployment.path().join("tamper.stderr");
let status = process::status(
Command::new(&executable)
.env_clear()
.current_dir(deployment.path())
.args(["media.tool.execute", "{}"])
.stdout(Stdio::null())
.stderr(Stdio::from(fs::File::create(&capture).unwrap())),
Duration::from_secs(10),
)
.unwrap();
assert!(!status.success());
assert!(fs::read_to_string(capture).unwrap().contains("resource"));
}
@@ -0,0 +1,291 @@
//! Explicit x86 compiler -> ARM application acceptance; QEMU user mode does
//! not establish the namespace/seccomp contract of external Workers.
use super::*;
use dever_cli::toolchain::BuildTarget;
use dever_cli::toolchain::compilation::{CompileKind, CompileRequest};
use dever_core::source::SourceMap;
use dever_runtime::config::Settings;
use std::io::{Read, Seek, SeekFrom, Write};
#[path = "cross_ecosystems.rs"]
mod ecosystems;
fn add_arm_inputs(author: &Path, workspace: &Path) {
let prepared = workspace.join("target/arm64-cross");
let mut profiles = serde_json::Map::new();
for profile in PROFILES {
let name = format!("{profile}.a");
profiles.insert(
profile.into(),
input(
author,
&prepared.join("archives").join(&name),
&format!("arm/{name}"),
),
);
}
let libc = prepared.join("tools/usr/aarch64-linux-gnu/lib");
let gcc = prepared.join("tools/usr/lib/gcc-cross/aarch64-linux-gnu/13");
let links = |root: &Path, names: &[&str]| -> Vec<Value> {
names
.iter()
.map(|name| {
let mut entry = input(author, &root.join(name), &format!("arm/{name}"));
entry["path"] = json!(name);
entry
})
.collect()
};
let start = [
links(&libc, &["crt1.o", "crti.o"]),
links(&gcc, &["crtbeginT.o"]),
]
.concat();
let libraries = [
links(&libc, &["libc.a", "libm-2.39.a", "libmvec.a"]),
links(&gcc, &["libgcc.a", "libgcc_eh.a"]),
]
.concat();
let end = [links(&gcc, &["crtend.o"]), links(&libc, &["crtn.o"])].concat();
let path = author.join("config/setting.json");
let mut settings: Value = serde_json::from_slice(&fs::read(&path).unwrap()).unwrap();
settings["native_targets"] = json!({"linux-aarch64": {"profiles": profiles, "start":start, "libraries":libraries, "end":end}});
fs::write(path, serde_json::to_vec(&settings).unwrap()).unwrap();
}
fn build(compiler: &Path, project: &Path, output: &Path, target: BuildTarget) {
command(
Command::new(compiler)
.arg("build")
.arg(project)
.args(["--target", target.platform(), "--output"])
.arg(output),
);
let bytes = fs::read(output).unwrap();
let elf = goblin::elf::Elf::parse(&bytes).unwrap();
assert!(elf.is_64 && elf.little_endian);
assert!(elf.interpreter.is_none() && elf.libraries.is_empty());
assert_eq!(
elf.header.e_machine,
match target {
BuildTarget::LinuxX86_64 => goblin::elf::header::EM_X86_64,
BuildTarget::LinuxAarch64 => goblin::elf::header::EM_AARCH64,
}
);
}
fn request(project: &Path, target: BuildTarget) -> CompileRequest {
let sources = SourceMap::load_with_packages(&project.join("module"), None, &[]).unwrap();
let bindings = Settings::load_project(project)
.unwrap()
.compilation_bindings();
CompileRequest::new(
env!("CARGO_PKG_VERSION").into(),
CompileKind::Application,
&sources,
Some(bindings),
&[],
target,
)
.unwrap()
}
fn private_compiler(
installed: &Path,
destination: &Path,
manifest: &dever_cli::toolchain::ReleaseManifest,
) -> PathBuf {
for artifact in &manifest.artifacts {
if artifact.path != "dever-core"
&& !artifact.path.starts_with("lib/")
&& !artifact.path.starts_with("runtime/linux-aarch64/")
{
continue;
}
let relative = if artifact.path == "dever-core" {
"dever"
} else {
&artifact.path
};
let output = destination.join(relative);
fs::create_dir_all(output.parent().unwrap()).unwrap();
fs::hard_link(installed.join(&artifact.path), output).unwrap();
}
destination.join("dever")
}
fn reject_tampered_cache_hit(layout: &Layout, archive: &Path, request: &CompileRequest) {
let mut file = fs::OpenOptions::new()
.read(true)
.write(true)
.open(archive)
.unwrap();
let mut original = [0; 1];
file.read_exact(&mut original).unwrap();
file.seek(SeekFrom::Start(0)).unwrap();
file.write_all(&[original[0] ^ 1]).unwrap();
file.sync_all().unwrap();
let rejected = dever_cli::toolchain::compile(layout, request);
file.seek(SeekFrom::Start(0)).unwrap();
file.write_all(&original).unwrap();
file.sync_all().unwrap();
assert!(rejected.unwrap_err().contains("integrity verification"));
}
fn export_http_application(compiler: &Path, projects: &Path, workspace: &Path) -> Value {
let project = projects.join("http");
for (path, source) in [
(
"user/account/model.dever",
"global type Account { name: Text(1, 32) }",
),
(
"platform/tenant/model.dever",
"global type Tenant { name: Text(1, 32) }",
),
(
"user/account/app.dever",
"type Identity { id: Text\nuser_id: user.account.model.id?\ntenant_id: platform.tenant.model.id? }\nverify(claims: dever.auth.Claims) (identity: Identity) { identity = Identity { id = claims.subject\nuser_id = null\ntenant_id = null } }",
),
(
"sample/http/app.dever",
"hello() (message: Text) { message = \"arm-http\" }",
),
("sample/http/api.dever", "public get hello = app.hello"),
] {
write(&project, &format!("module/{path}"), source);
}
let settings = serde_json::to_vec(&json!({
"http":{"host":"127.0.0.1","port":18080},
"runtime":{"mode":"api","shutdown_ms":2000}, "log":{"level":"error"},
"database":{"default":{"type":"sqlite","path":"data/http.sqlite","max_connections":1}},
"auth":{"providers":{"session":{"verify":"user.account.verify","jwtSecret":"cross-acceptance-only-test-key-2026"}}},
"sites":{"test":{"path":"","auth":"session"}}
})).unwrap();
write(&project, "config/setting.json", &settings);
let program = project.join("application");
build(compiler, &project, &program, BuildTarget::LinuxAarch64);
let exported = workspace.join("target/arm64-cross/guest/apps/http");
write(&exported, "config/setting.json", settings);
fs::copy(&program, exported.join("application")).unwrap();
json!({"sha256":sha256_file(&program).unwrap(), "profile":"sqlite", "url":"http://127.0.0.1:18080/sample/http/hello", "expected_data":"arm-http", "execution":"pending_guest_validation"})
}
#[test]
#[ignore = "requires prepared x86/ARM four-profile runtime archives and private QEMU; starts only an owned daemon"]
fn signed_host_compiler_builds_arm_profiles_and_runs_standalone_under_qemu() {
let workspace = Path::new(env!("CARGO_MANIFEST_DIR"))
.join("../..")
.canonicalize()
.unwrap();
let emulator = workspace.join("target/arm64-cross/tools/usr/bin/qemu-aarch64-static");
assert!(
emulator.is_file(),
"prepare the task-owned static QEMU emulator"
);
let author = TemporaryDirectory::new();
let key = author_inputs(author.path(), &workspace);
add_arm_inputs(author.path(), &workspace);
let machine = machine_temporary_directory(&workspace);
let layout = Layout::new(machine.path());
layout.initialize().unwrap();
let version = Version::parse(env!("CARGO_PKG_VERSION")).unwrap();
let release = layout.downloads().join(version.as_str());
let manifest = packaging::create(author.path(), &release).unwrap();
assert_eq!(manifest.platform, BuildTarget::LinuxX86_64.platform());
install_release(&layout, &version, &key);
fs::remove_dir_all(&release).unwrap();
drop(author);
let compiler = layout.versions().join(version.as_str()).join("dever-core");
let mut daemon = daemon::TestDaemon::start(&layout);
let projects = TemporaryDirectory::new();
let standalone = TemporaryDirectory::new();
let mut programs = Vec::new();
let mut evidence = serde_json::Map::new();
for profile in PROFILES {
let project = projects.path().join(profile);
application(&project, profile);
let destination = standalone.path().join(profile);
fs::create_dir_all(destination.join("config")).unwrap();
fs::copy(
project.join("config/setting.json"),
destination.join("config/setting.json"),
)
.unwrap();
let executable = destination.join("application");
build(&compiler, &project, &executable, BuildTarget::LinuxAarch64);
let before = cache_status(&layout).unwrap().entries;
let repeated = project.join("repeated");
build(&compiler, &project, &repeated, BuildTarget::LinuxAarch64);
assert_eq!(
sha256_file(&executable).unwrap(),
sha256_file(&repeated).unwrap()
);
assert_eq!(cache_status(&layout).unwrap().entries, before);
evidence.insert(profile.into(), json!({"sha256":sha256_file(&executable).unwrap(), "bytes":fs::metadata(&executable).unwrap().len(), "cache_hit":true}));
programs.push((destination, executable));
}
assert_eq!(cache_status(&layout).unwrap().entries, 4);
let base_project = projects.path().join("base");
let host = standalone.path().join("base/host-application");
build(&compiler, &base_project, &host, BuildTarget::LinuxX86_64);
assert_response(command(
Command::new(&host).args(["sample.value.exercise", "{}"]),
));
assert_eq!(cache_status(&layout).unwrap().entries, 5);
let private = private_compiler(
compiler.parent().unwrap(),
&machine.path().join("private"),
&manifest,
);
let private_directory = standalone.path().join("private-base");
write(&private_directory, "config/setting.json", b"{}");
let private_output = private_directory.join("application");
build(
&private,
&base_project,
&private_output,
BuildTarget::LinuxAarch64,
);
programs.push((private_directory, private_output));
// Use the same canonical request to prove integrity is checked before a hit.
let arm_request = request(&base_project, BuildTarget::LinuxAarch64);
assert_eq!(
dever_cli::toolchain::compile(&layout, &arm_request).unwrap(),
fs::read(&programs[0].1).unwrap()
);
let archive = compiler
.parent()
.unwrap()
.join("runtime/linux-aarch64/archives/base.a");
reject_tampered_cache_hit(&layout, &archive, &arm_request);
assert_eq!(cache_status(&layout).unwrap().entries, 5);
let http = export_http_application(&compiler, projects.path(), &workspace);
assert_eq!(cache_status(&layout).unwrap().entries, 6);
daemon.stop();
drop(projects);
drop(machine);
for (directory, executable) in programs {
assert!(!directory.join("module").exists());
assert_response(command(
Command::new(&emulator)
.arg(executable)
.current_dir(directory)
.args(["sample.value.exercise", "{}"]),
));
}
let report = json!({
"format":"dever-cross-application-acceptance-v1", "host":"linux-x86_64", "target":"linux-aarch64",
"programs":evidence, "http":http, "qemu_sha256":sha256_file(&emulator).unwrap(),
"checks":{"signed_install":true,"profiles":4,"private_arm_build":true,"standalone_after_source_and_machine_removal":true,"sqlite_crud":true,"cache_entries":6,"cache_target_isolation":true,"tampered_target_cache_hit_rejected":true},
"postgres_server":"not_run", "worker_sandbox":"requires_arm_kernel_system_emulation"
});
write(
&workspace,
"target/arm64-cross/compile-acceptance.json",
serde_json::to_vec_pretty(&report).unwrap(),
);
}
@@ -0,0 +1,348 @@
//! Produce real standalone ARM Worker applications for full-system acceptance.
use super::*;
use dever_cli::libs::{
self, LibSpec, LockFile, LockedArtifact, LockedLib, LockedWorker, RegistryRuntime,
};
use dever_cli::toolchain::{Artifact, ReleaseManifest};
use dever_core::hir::ExternalWorkerContract;
use sha2::{Digest, Sha256};
use std::collections::BTreeMap;
const ECOSYSTEMS: [&str; 3] = ["pip", "npm", "go"];
const TARGET: BuildTarget = BuildTarget::LinuxAarch64;
fn digest(bytes: &[u8]) -> String {
format!("{:x}", Sha256::digest(bytes))
}
fn add_target_sandbox(author: &Path, workspace: &Path) {
let root = workspace.join("target/arm64-cross/sandbox");
let mut files = Vec::new();
let mut inputs = Vec::new();
for directory in ["bin", "lib"] {
for entry in fs::read_dir(root.join(directory)).unwrap() {
let entry = entry.unwrap();
assert!(entry.file_type().unwrap().is_file());
let relative = format!("{directory}/{}", entry.file_name().to_str().unwrap());
let bytes = fs::read(entry.path()).unwrap();
let source = format!("inputs/arm-sandbox/{relative}");
write(author, &source, &bytes);
inputs.push(json!({"source":source,"path":relative,"sha256":digest(&bytes)}));
files.push((relative, bytes));
}
}
dever_sandbox::validate_assets_for_target(&files, TARGET.platform()).unwrap();
let path = author.join("config/setting.json");
let mut settings: Value = serde_json::from_slice(&fs::read(&path).unwrap()).unwrap();
settings["sandbox_targets"] = json!({"linux-aarch64":inputs});
fs::write(path, serde_json::to_vec(&settings).unwrap()).unwrap();
}
// Sign the exact prepared archives: their identities also bind each Lib lock.
// As in the native ecosystem fixture, this is an explicit private test author.
fn sign_target_runtimes(
release: &Path,
workspace: &Path,
key: &Ed25519KeyPair,
ecosystems: &[&str],
) -> BTreeMap<String, RegistryRuntime> {
let mut manifest: ReleaseManifest =
serde_json::from_slice(&fs::read(release.join("manifest.json")).unwrap()).unwrap();
let mut runtimes = BTreeMap::new();
for &ecosystem in ecosystems {
let prepared = workspace
.join("target/arm64-cross/ecosystems")
.join(ecosystem);
let settings: Value =
serde_json::from_slice(&fs::read(prepared.join("config/setting.json")).unwrap())
.unwrap();
let runtime: RegistryRuntime = serde_json::from_value(settings["runtime"].clone()).unwrap();
assert_eq!(runtime.target, TARGET.platform());
let pack = fs::read(prepared.join("runtime.pack")).unwrap();
assert_eq!(digest(&pack), runtime.pack.sha256);
let prefix = format!("runtime/{ecosystem}/{}", TARGET.platform());
write(release, &format!("{prefix}/runtime.pack"), pack);
write(
release,
&format!("{prefix}/manifest.json"),
serde_json::to_vec(&json!({"format":"dever-registry-runtime-v1","runtime":runtime}))
.unwrap(),
);
for leaf in ["manifest.json", "runtime.pack"] {
let path = format!("{prefix}/{leaf}");
assert!(
!manifest
.artifacts
.iter()
.any(|artifact| artifact.path == path)
);
manifest.artifacts.push(Artifact {
bytes: fs::metadata(release.join(&path)).unwrap().len(),
sha256: sha256_file(&release.join(&path)).unwrap(),
path,
});
}
runtimes.insert(ecosystem.to_owned(), runtime);
}
manifest
.artifacts
.sort_by(|left, right| left.path.cmp(&right.path));
let encoded = serde_json::to_vec(&manifest).unwrap();
fs::write(release.join("manifest.json"), &encoded).unwrap();
fs::write(
release.join("manifest.sig"),
key.sign(&encoded)
.as_ref()
.iter()
.map(|byte| format!("{byte:02x}"))
.collect::<String>(),
)
.unwrap();
runtimes
}
fn application(root: &Path, ecosystem: &str) -> ExternalWorkerContract {
let (extension, library, source) = match ecosystem {
"pip" => (
"py",
"six@1.17.0",
include_str!("../../ecosystem-release/arm64/probe.py"),
),
"npm" => (
"mjs",
"is-number@7.0.0",
include_str!("../../ecosystem-release/arm64/probe.mjs"),
),
"go" => (
"go",
"github.com/google/uuid@1.6.0",
include_str!("../../ecosystem-release/arm64/probe.go"),
),
_ => unreachable!(),
};
write(root, "config/setting.json", "{}\n");
write(
root,
"module/sample/worker/port.dever",
"probe() (okay: Bool) fails app.ProbeResult\nreject() (okay: Bool) fails app.ProbeResult\n",
);
write(
root,
"module/sample/worker/app.dever",
"type ProbeResult { error Unavailable(message: Text) }\nprobe() (okay: Bool) { okay = port.probe() }\nreject() (okay: Bool) { okay = port.reject() }\n",
);
write(
root,
"module/sample/worker/api.dever",
"cmd probe = app.probe\ncmd reject = app.reject\n",
);
write(
root,
"module/sample/worker/adapter.dever",
format!("external {ecosystem} \"worker/probe.{extension}\" {{ lib \"{library}\" }}\n"),
);
let sources = SourceMap::load(&root.join("module")).unwrap();
let program = dever_core::check(&sources).unwrap_or_else(|errors| panic!("{errors:?}"));
let contract = program.external_worker_contracts().remove(0);
write(root, &contract.entry, source);
contract
}
fn pure_package_lock(
workspace: &Path,
contract: &ExternalWorkerContract,
runtime: &RegistryRuntime,
) -> (LockFile, BTreeMap<String, Vec<u8>>) {
let spec: LibSpec = contract.libs[0].parse().unwrap();
let (filename, extension) = match contract.ecosystem.as_str() {
"pip" => ("six-1.17.0-py2.py3-none-any.whl", "whl"),
"npm" => ("is-number-7.0.0.tgz", "tgz"),
_ => unreachable!(),
};
let bytes = fs::read(
workspace
.join("target/arm64-cross/dependencies")
.join(filename),
)
.unwrap();
let sha256 = digest(&bytes);
let artifact = LockedArtifact {
target: TARGET.platform().into(),
path: format!(
"lib/{}/{}/{}/archive.{extension}",
contract.ecosystem,
digest(spec.distribution_name().as_bytes()),
spec.version
),
bytes: bytes.len() as u64,
sha256: sha256.clone(),
};
let mut lock = LockFile::new(vec![LockedLib {
spec: spec.clone(),
dependencies: vec![],
runtime: runtime.pack.clone(),
artifacts: vec![artifact],
schema: contract.schema.clone(),
build: None,
}])
.unwrap();
if contract.ecosystem == "npm" {
lock.npm.push(libs::npm::Environment::single(spec));
}
(lock, BTreeMap::from([(sha256, bytes)]))
}
fn go_package_lock(
workspace: &Path,
runtime: &RegistryRuntime,
) -> (LockFile, BTreeMap<String, Vec<u8>>) {
let prepared = workspace.join("target/go-managed/dependency");
let mut lock = LockFile::decode(&fs::read(prepared.join("dever.lock")).unwrap()).unwrap();
libs::doctor(&lock).unwrap();
assert!(lock.builds.is_empty() && lock.workers.is_empty());
assert_eq!(lock.libs.len(), 1);
assert_eq!(lock.libs[0].spec.key(), "go:github.com/google/uuid@1.6.0");
let authenticated =
libs::sumdb::verify_chain(&lock.go_sumdb, &libs::sumdb::Verifier::official()).unwrap();
let mut archives = BTreeMap::new();
for library in &mut lock.libs {
// Go module ZIPs are source-only; keep the verified source and sumdb
// evidence unchanged while selecting the new runtime and build target.
library.runtime = runtime.pack.clone();
for artifact in &mut library.artifacts {
let bytes = fs::read(prepared.join("artifacts").join(&artifact.sha256)).unwrap();
assert_eq!(digest(&bytes), artifact.sha256);
assert_eq!(bytes.len() as u64, artifact.bytes);
authenticated
.verify_zip(&library.spec.name, &library.spec.version, &bytes)
.unwrap();
artifact.target = TARGET.platform().into();
archives.insert(artifact.sha256.clone(), bytes);
}
}
(lock, archives)
}
fn prepare_locked_application(
project: &Path,
workspace: &Path,
layout: &Layout,
contract: ExternalWorkerContract,
runtime: &RegistryRuntime,
) -> LockFile {
let (mut lock, archives) = if contract.ecosystem == "go" {
go_package_lock(workspace, runtime)
} else {
pure_package_lock(workspace, &contract, runtime)
};
for (sha256, bytes) in archives {
let receipt = dever_cli::toolchain::artifact_put(layout, &bytes).unwrap();
assert_eq!(receipt.sha256, sha256);
assert_eq!(receipt.bytes, bytes.len() as u64);
}
lock.workers.push(LockedWorker {
libs: contract
.libs
.iter()
.map(|value| value.parse().unwrap())
.collect(),
port: contract.port,
adapter: contract.adapter,
ecosystem: contract.ecosystem,
runtime: Some(runtime.pack.clone()),
entry: contract.entry,
schema: contract.schema,
capabilities: contract.capabilities,
operations: contract.operations,
});
libs::doctor(&lock).unwrap();
lock.write_atomic(project).unwrap();
lock
}
#[test]
#[ignore = "requires explicit ARM runtimes/dependencies/sandbox and native archives; builds with an owned host daemon and exports apps for ARM system QEMU"]
fn signed_arm_ecosystems_build_offline_standalone_apps_for_system_guest() {
build_guest_apps(&ECOSYSTEMS, "ecosystem-build-acceptance.json");
}
#[test]
#[ignore = "requires explicit ARM inputs; rebuilds only Python for SDK shutdown regression under the ARM guest"]
fn signed_arm_python_worker_rebuilds_with_current_sdk() {
build_guest_apps(&["pip"], "python-build-acceptance.json");
}
fn build_guest_apps(ecosystems: &[&str], report: &str) {
let workspace = Path::new(env!("CARGO_MANIFEST_DIR"))
.join("../..")
.canonicalize()
.unwrap();
let destination = workspace.join("target/arm64-cross/guest/apps");
fs::create_dir_all(&destination).unwrap();
for &ecosystem in ecosystems {
assert!(
!destination.join(ecosystem).exists(),
"guest app already exists; preserve or remove the prior owned fixture explicitly"
);
}
let author = TemporaryDirectory::new();
let key = author_inputs(author.path(), &workspace);
add_arm_inputs(author.path(), &workspace);
add_target_sandbox(author.path(), &workspace);
let machine = machine_temporary_directory(&workspace);
let layout = Layout::new(machine.path());
layout.initialize().unwrap();
let version = Version::parse(env!("CARGO_PKG_VERSION")).unwrap();
let release = layout.downloads().join(version.as_str());
packaging::create(author.path(), &release).unwrap();
let runtimes = sign_target_runtimes(&release, &workspace, &key, ecosystems);
install_release(&layout, &version, &key);
fs::remove_dir_all(&release).unwrap();
drop(author);
let compiler = layout.versions().join(version.as_str()).join("dever-core");
let mut daemon = daemon::TestDaemon::start(&layout);
let projects = TemporaryDirectory::new();
let mut programs = serde_json::Map::new();
for &ecosystem in ecosystems {
let project = projects.path().join(ecosystem);
let contract = application(&project, ecosystem);
let lock = prepare_locked_application(
&project,
&workspace,
&layout,
contract,
&runtimes[ecosystem],
);
command(
Command::new(&compiler)
.args(["lib", "doctor"])
.arg(&project)
.args(["--target", TARGET.platform()]),
);
let output = destination.join(ecosystem);
fs::create_dir(&output).unwrap();
write(&output, "config/setting.json", "{}\n");
let executable = output.join("program");
build(&compiler, &project, &executable, TARGET);
programs.insert(ecosystem.into(), json!({
"path":format!("{ecosystem}/program"), "sha256":sha256_file(&executable).unwrap(),
"bytes":fs::metadata(&executable).unwrap().len(), "runtime":runtimes[ecosystem].pack,
"libs":lock.libs.iter().map(|lib| lib.spec.key()).collect::<Vec<_>>(),
"artifacts":lock.libs.iter().flat_map(|lib| &lib.artifacts).collect::<Vec<_>>()
}));
}
assert_eq!(
cache_status(&layout).unwrap().entries,
ecosystems.len() as u64
);
daemon.stop();
drop(projects);
drop(machine);
write(&workspace, &format!("target/arm64-cross/{report}"), serde_json::to_vec_pretty(&json!({
"format":"dever-cross-ecosystem-build-v1", "target":TARGET.platform(), "programs":programs,
"checks":{"signed_runtime_install":true,"offline_lib_doctor":ecosystems.len(),"actual_cli_arm_build":ecosystems.len(),"source_and_build_machine_removed":true},
"guest_probe":{"arguments":["sample.worker.probe","{}"],"expected_data":true},
"guest_failure":{"arguments":["sample.worker.reject","{}"],"expected":"runtime fault; next fresh probe still succeeds; no remaining Worker"},
"guest_execution":"not_run", "sandbox_execution":"requires full ARM kernel guest"
})).unwrap());
}
@@ -0,0 +1,398 @@
//! Explicit signed-release acceptance with no host language trees in the final root.
use super::*;
use std::os::unix::fs::{MetadataExt, chown};
const ECOSYSTEMS: [&str; 3] = ["pip", "npm", "go"];
const STANDALONE_UID: u32 = 65534;
fn nonroot_standalone(source: &Path, expected_program: &Value) -> Value {
let deployment = TemporaryDirectory::new_in(Path::new("/tmp"));
let program = deployment.path().join("program");
let config = deployment.path().join("config");
let settings = config.join("setting.json");
fs::create_dir(&config).unwrap();
fs::copy(source.join("program"), &program).unwrap();
fs::copy(source.join("config/setting.json"), &settings).unwrap();
let identity = json!({
"bytes": fs::metadata(&program).unwrap().len(),
"sha256": sha256_file(&program).unwrap(),
});
assert_eq!(&identity, expected_program);
for (path, mode) in [
(program.as_path(), 0o700),
(config.as_path(), 0o700),
(settings.as_path(), 0o600),
(deployment.path(), 0o700),
] {
fs::set_permissions(path, fs::Permissions::from_mode(mode)).unwrap();
chown(path, Some(STANDALONE_UID), Some(STANDALONE_UID)).unwrap();
}
// Only the standalone executable and private config cross this boundary.
// The actual non-root process must extract its own embedded Worker bundle.
assert!(!deployment.path().join("data").exists());
let output = command(
Command::new("/usr/bin/setpriv")
.arg(format!("--reuid={STANDALONE_UID}"))
.arg(format!("--regid={STANDALONE_UID}"))
.args([
"--clear-groups",
"--inh-caps=-all",
"--ambient-caps=-all",
"--",
])
.arg(&program)
.args(["sample.worker.probe", "{}"])
.current_dir(deployment.path())
.stdin(Stdio::null()),
);
eprintln!(
"standalone UID {STANDALONE_UID} {}: {}\n{}",
source.display(),
String::from_utf8_lossy(&output.stdout),
String::from_utf8_lossy(&output.stderr)
);
assert_response(output);
assert_eq!(
fs::metadata(deployment.path().join("data/cache/lib"))
.unwrap()
.uid(),
STANDALONE_UID
);
json!({"passed": true, "uid": STANDALONE_UID, "program": identity})
}
fn dependency_fixture(workspace: &Path, ecosystem: &str) -> PathBuf {
workspace.join(match ecosystem {
"pip" => "target/source-build",
"npm" => "target/npm-build",
_ => unreachable!(),
})
}
/// The fixtures were built against these exact pack bytes. The test author
/// signs those inputs, rather than rewriting receipts for a recompressed pack.
fn sign_build_runtimes(
release: &Path,
workspace: &Path,
key: &Ed25519KeyPair,
) -> dever_cli::toolchain::ReleaseManifest {
use dever_cli::libs::{LockFile, RegistryRuntime, RuntimePack};
let mut manifest: dever_cli::toolchain::ReleaseManifest =
serde_json::from_slice(&fs::read(release.join("manifest.json")).unwrap()).unwrap();
for ecosystem in ["pip", "npm"] {
let fixture = dependency_fixture(workspace, ecosystem);
let settings: Value =
serde_json::from_slice(&fs::read(fixture.join("config/setting.json")).unwrap())
.unwrap();
let runtime: RegistryRuntime = serde_json::from_value(settings["runtime"].clone()).unwrap();
assert_eq!(runtime.target, "linux-x86_64");
let pack = fixture.join(settings["runtime_file"].as_str().unwrap());
assert_eq!(sha256_file(&pack).unwrap(), runtime.pack.sha256);
let lock =
LockFile::decode(&fs::read(fixture.join("dependency/dever.lock")).unwrap()).unwrap();
assert!(
!lock.builds.is_empty(),
"acceptance requires actual source build receipts"
);
for receipt in &lock.builds {
assert_eq!(receipt.runtime, runtime.pack);
if let Some(python) = &receipt.auxiliary_runtime {
let python_config: Value = serde_json::from_slice(
&fs::read(dependency_fixture(workspace, "pip").join("config/setting.json"))
.unwrap(),
)
.unwrap();
assert_eq!(
*python,
serde_json::from_value::<RuntimePack>(python_config["runtime"]["pack"].clone())
.unwrap()
);
}
}
let prefix = format!("runtime/{ecosystem}/linux-x86_64");
fs::copy(pack, release.join(&prefix).join("runtime.pack")).unwrap();
fs::write(
release.join(&prefix).join("manifest.json"),
serde_json::to_vec(&json!({"format":"dever-registry-runtime-v1","runtime":runtime}))
.unwrap(),
)
.unwrap();
for suffix in ["runtime.pack", "manifest.json"] {
let name = format!("{prefix}/{suffix}");
let artifact = manifest
.artifacts
.iter_mut()
.find(|artifact| artifact.path == name)
.unwrap();
artifact.bytes = fs::metadata(release.join(&name)).unwrap().len();
artifact.sha256 = sha256_file(&release.join(&name)).unwrap();
}
}
let encoded = serde_json::to_vec(&manifest).unwrap();
fs::write(release.join("manifest.json"), &encoded).unwrap();
fs::write(
release.join("manifest.sig"),
key.sign(&encoded)
.as_ref()
.iter()
.map(|byte| format!("{byte:02x}"))
.collect::<String>(),
)
.unwrap();
manifest
}
fn import_built_dependencies(project: &Path, fixture: &Path, layout: &Layout) {
use dever_cli::libs::{LockFile, LockedWorker, build};
let mut lock =
LockFile::decode(&fs::read(fixture.join("dependency/dever.lock")).unwrap()).unwrap();
for artifact in lock
.libs
.iter()
.flat_map(|lib| &lib.artifacts)
.chain(build::npm_outputs(&lock).map(|receipt| &receipt.output))
{
let bytes = fs::read(fixture.join("dependency/artifacts").join(&artifact.sha256)).unwrap();
let stored = dever_cli::toolchain::artifact_put(layout, &bytes).unwrap();
assert_eq!(stored.sha256, artifact.sha256);
assert_eq!(stored.bytes, artifact.bytes);
}
let sources = dever_core::source::SourceMap::load(&project.join("module")).unwrap();
let program = dever_core::check(&sources).unwrap_or_else(|errors| panic!("{errors:?}"));
let contract = program.external_worker_contracts().remove(0);
let runtime = lock.libs[0].runtime.clone();
assert!(lock.libs.iter().all(|lib| lib.runtime == runtime));
assert!(lock.workers.is_empty());
lock.workers.push(LockedWorker {
port: contract.port,
adapter: contract.adapter,
ecosystem: contract.ecosystem,
runtime: Some(runtime),
entry: contract.entry,
schema: contract.schema,
capabilities: contract.capabilities,
operations: contract.operations,
libs: contract
.libs
.iter()
.map(|spec| spec.parse().unwrap())
.collect(),
});
dever_cli::libs::doctor(&lock).unwrap();
lock.write_atomic(project).unwrap();
}
fn ecosystem_inputs(author: &Path, workspace: &Path) {
let prepared = workspace.join("target/ecosystem-release-inputs/prepared");
let fragment: Value =
serde_json::from_slice(&fs::read(prepared.join("config/setting.json")).unwrap()).unwrap();
for ecosystem in ECOSYSTEMS {
for entry in fragment["runtimes"][ecosystem]["files"].as_array().unwrap() {
let relative = entry["source"].as_str().unwrap();
let output = author.join(relative);
fs::create_dir_all(output.parent().unwrap()).unwrap();
// Author staging only; the maker materializes its own immutable pack.
fs::hard_link(prepared.join(relative), output).unwrap();
}
}
let config = author.join("config/setting.json");
let mut settings: Value = serde_json::from_slice(&fs::read(&config).unwrap()).unwrap();
settings["runtimes"] = fragment["runtimes"].clone();
fs::write(config, serde_json::to_vec(&settings).unwrap()).unwrap();
}
fn application(root: &Path, ecosystem: &str) {
let (extension, source) = match ecosystem {
"pip" => ("py", include_str!("../../ecosystem-release/probe.py")),
"npm" => ("mjs", include_str!("../../ecosystem-release/probe.mjs")),
"go" => ("go", include_str!("../../ecosystem-release/probe.go")),
_ => unreachable!(),
};
write(root, "config/setting.json", "{}\n");
write(
root,
"module/sample/worker/port.dever",
"probe() (okay: Bool) fails app.ProbeResult\n",
);
write(
root,
"module/sample/worker/app.dever",
"type ProbeResult { error Unavailable(message: Text) }\nprobe() (okay: Bool) { okay = port.probe() }\n",
);
write(
root,
"module/sample/worker/api.dever",
"cmd probe = app.probe\n",
);
write(
root,
"module/sample/worker/adapter.dever",
format!(
"external {ecosystem} \"worker/probe.{extension}\" {{\n{}\n}}\n",
match ecosystem {
"pip" => "lib \"simplejson@3.20.1\"",
"npm" => "lib \"bufferutil@4.0.9\"",
"go" => "",
_ => unreachable!(),
}
),
);
let sources = dever_core::source::SourceMap::load(&root.join("module")).unwrap();
let program = dever_core::check(&sources).unwrap_or_else(|errors| panic!("{errors:?}"));
let entry = &program.external_worker_contracts()[0].entry;
write(root, entry, source);
}
pub(super) fn minimal_os(root: &Path) {
// This root contains only the named Linux OS ABI libraries. No host language
// executable, standard library, PATH, package manager or source tree is visible.
for name in [
"libc.so.6",
"libdl.so.2",
"libm.so.6",
"libpthread.so.0",
"librt.so.1",
"libutil.so.1",
"libresolv.so.2",
"ld-linux-x86-64.so.2",
] {
let source = Path::new("/lib/x86_64-linux-gnu").join(name);
let output = root.join("lib/x86_64-linux-gnu").join(name);
fs::create_dir_all(output.parent().unwrap()).unwrap();
fs::copy(source, output).unwrap();
}
fs::create_dir(root.join("lib64")).unwrap();
fs::copy(
"/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2",
root.join("lib64/ld-linux-x86-64.so.2"),
)
.unwrap();
fs::create_dir(root.join("tmp")).unwrap();
fs::create_dir(root.join("proc")).unwrap();
fs::create_dir(root.join("dev")).unwrap();
// The outer namespace supplies only its own proc and synthetic device tree.
assert!(!root.join("usr").exists());
}
#[test]
#[ignore = "requires prepared pinned ecosystem inputs and root for owned namespace; no existing services"]
fn signed_ecosystems_build_and_run_without_system_language_installations() {
assert_eq!(
rustix::process::geteuid().as_raw(),
0,
"explicit no-host acceptance requires the owned root namespace fixture"
);
let workspace = Path::new(env!("CARGO_MANIFEST_DIR")).join("../..");
let projects = TemporaryDirectory::new();
for ecosystem in ECOSYSTEMS {
application(&projects.path().join(ecosystem), ecosystem);
}
let author = TemporaryDirectory::new();
let key = author_inputs(author.path(), &workspace);
ecosystem_inputs(author.path(), &workspace);
let machine = machine_temporary_directory(&workspace);
let layout = Layout::new(machine.path());
layout.initialize().unwrap();
let version = Version::parse(env!("CARGO_PKG_VERSION")).unwrap();
let release = layout.downloads().join(version.as_str());
command_with_timeout(
Command::new(workspace.join("target/debug/examples/native-release"))
.arg(author.path())
.arg("--output")
.arg(&release),
// Debug gzip compresses complete language distributions; this is author
// preparation, independent of the service's bounded compile deadline.
Duration::from_secs(900),
);
let signed = sign_build_runtimes(&release, &workspace, &key);
let mut runtimes = serde_json::Map::new();
for ecosystem in ECOSYSTEMS {
let path = format!("runtime/{ecosystem}/linux-x86_64/runtime.pack");
let artifact = signed
.artifacts
.iter()
.find(|artifact| artifact.path == path)
.unwrap();
runtimes.insert(
ecosystem.into(),
json!({"bytes":artifact.bytes,"sha256":artifact.sha256}),
);
}
install_release(&layout, &version, &key);
fs::remove_dir_all(&release).unwrap();
drop(author);
let core = layout.versions().join(version.as_str()).join("dever-core");
let mut daemon = daemon::TestDaemon::start(&layout);
let isolated = TemporaryDirectory::new();
minimal_os(isolated.path());
let mut programs = serde_json::Map::new();
for ecosystem in ECOSYSTEMS {
let project = projects.path().join(ecosystem);
if ecosystem == "go" {
command(Command::new(&core).args(["lib", "add"]).arg(&project));
} else {
import_built_dependencies(
&project,
&dependency_fixture(&workspace, ecosystem),
&layout,
);
}
let lock =
dever_cli::libs::LockFile::decode(&fs::read(project.join("dever.lock")).unwrap())
.unwrap();
assert_eq!(lock.workers.len(), 1);
assert_eq!(lock.libs.is_empty(), ecosystem == "go");
assert_eq!(lock.builds.is_empty(), ecosystem == "go");
assert_eq!(
lock.workers[0].runtime.as_ref().unwrap().sha256,
runtimes[ecosystem]["sha256"].as_str().unwrap()
);
command(Command::new(&core).arg("check").arg(&project));
assert_response(command(
Command::new(&core)
.arg("run")
.arg(&project)
.args(["--", "sample.worker.probe", "{}"]),
));
let destination = isolated.path().join(ecosystem);
fs::create_dir(&destination).unwrap();
write(&destination, "config/setting.json", "{}\n");
let executable = destination.join("program");
command(
Command::new(&core)
.arg("build")
.arg(&project)
.arg("--output")
.arg(&executable),
);
programs.insert(ecosystem.into(), json!({"bytes":fs::metadata(&executable).unwrap().len(),"sha256":sha256_file(&executable).unwrap()}));
}
assert_eq!(cache_status(&layout).unwrap().entries, 3);
daemon.stop();
drop(machine);
drop(projects);
for ecosystem in ECOSYSTEMS {
assert_response(command(
isolated_os_command(isolated.path())
.arg(format!("/{ecosystem}/program"))
.args(["sample.worker.probe", "{}"]),
));
}
let mut standalone_nonroot = serde_json::Map::new();
for ecosystem in ECOSYSTEMS {
standalone_nonroot.insert(
ecosystem.into(),
nonroot_standalone(&isolated.path().join(ecosystem), &programs[ecosystem]),
);
}
write(&workspace, "target/closure-sandbox-ecosystem-release-acceptance.json", serde_json::to_vec_pretty(&json!({
"format":"dever-ecosystem-release-acceptance-v1", "platform":"linux-x86_64",
"runtimes":runtimes,"programs":programs,"standalone_nonroot":standalone_nonroot,
"checks":{"signed_install":true,"locked_runtimes":3,"managed_check_run_build":3,"standalone_isolated_root":3,"standalone_nonroot":3,"shared_compile_entries":3,"python_stdlib_paths_in_bundle":true,"os_sandbox":true,"offline_build_receipt_replay":true},
"os_baseline":"explicit Linux GNU OS libraries and named device nodes inside an owned root",
"runtime_pack_provenance":"test author signed the exact pip/npm packs bound to the completed source builds; maker determinism is verified separately",
"third_party_packages":{"pip":"simplejson@3.20.1 _speedups C extension","npm":"bufferutil@4.0.9 build/Release addon"}, "other_platforms":"not_run", "public_release":"not_published"
})).unwrap());
}
@@ -0,0 +1,429 @@
//! Synthetic runtime closures exercise the production maker without host interpreters.
use std::io::Read;
use super::*;
fn add_file(author: &mut Author, ecosystem: &str, path: &str, bytes: &[u8]) {
let source = format!("inputs/{ecosystem}/{path}");
let file = author.root.join(&source);
fs::create_dir_all(file.parent().unwrap()).unwrap();
fs::write(&file, bytes).unwrap();
author.settings["runtimes"][ecosystem]["files"]
.as_array_mut()
.unwrap()
.push(json!({"source": source, "path": path, "sha256": sha256_file(&file).unwrap()}));
}
fn set_manifest(author: &mut Author, ecosystem: &str, change: impl FnOnce(&mut Value)) {
let input = author.settings["runtimes"][ecosystem]["files"]
.as_array_mut()
.unwrap()
.iter_mut()
.find(|input| input["path"] == "dever-runtime.json")
.unwrap();
let path = author.root.join(input["source"].as_str().unwrap());
let mut manifest: Value = serde_json::from_slice(&fs::read(&path).unwrap()).unwrap();
change(&mut manifest);
fs::write(&path, serde_json::to_vec(&manifest).unwrap()).unwrap();
input["sha256"] = json!(sha256_file(&path).unwrap());
}
fn runtime_author() -> Author {
let mut author = Author::new();
for (ecosystem, name, version) in [
("pip", "cpython", "3.12.0"),
("npm", "node", "24.0.0"),
("go", "go", "1.25.0"),
] {
author.settings["runtimes"][ecosystem] = json!({"name":name,"version":version,"files":[]});
let mut descriptor = json!({"format":"dever-worker-runtime-v1","ecosystem":ecosystem,"target":platform_identity()});
if ecosystem == "go" {
let arch = if std::env::consts::ARCH == "x86_64" {
"amd64"
} else {
"arm64"
};
descriptor["build"] = json!({"host":platform_identity(),"compiler":"bin/compile","linker":"bin/link","analyzer":"bin/analyze","stdlib_importcfg":"stdlib/importcfg","goos":"linux","goarch":arch,"go_version":format!("go version go1.25.0 linux/{arch}")});
for path in ["bin/compile", "bin/link", "bin/analyze"] {
add_file(
&mut author,
ecosystem,
path,
&native_elf::static_executable(),
);
}
let mut importcfg = String::new();
for name in ["runtime", "context", "encoding/json"] {
let path = format!("stdlib/{name}.a");
let object = format!("go object linux {arch} go1.25.0 fixture\n");
let archive = format!(
"!<arch>\n{:<16}{:<12}{:<6}{:<6}{:<8}{:<10}`\n{object}",
"__.PKGDEF",
0,
0,
0,
"644",
object.len()
);
add_file(&mut author, ecosystem, &path, archive.as_bytes());
importcfg.push_str(&format!("packagefile {name}={path}\n"));
}
add_file(
&mut author,
ecosystem,
"stdlib/importcfg",
importcfg.as_bytes(),
);
} else {
descriptor["executable"] = json!(format!("bin/{name}"));
descriptor["arguments"] = if ecosystem == "pip" {
json!(["-I", "-S", "-B"])
} else {
json!([])
};
add_file(
&mut author,
ecosystem,
&format!("bin/{name}"),
&native_elf::static_executable(),
);
}
add_file(
&mut author,
ecosystem,
"dever-runtime.json",
&serde_json::to_vec(&descriptor).unwrap(),
);
}
author.settings["runtimes"]["pip"]["python_markers"] = json!({
"implementation_name":"cpython","implementation_version":"3.12.0","os_name":"posix",
"platform_machine":std::env::consts::ARCH,"platform_python_implementation":"CPython",
"platform_release":"","platform_system":"Linux","platform_version":"",
"python_full_version":"3.12.0","python_version":"3.12","sys_platform":"linux"
});
author.settings["runtimes"]["pip"]["python_wheel_tags"] = json!(["py3-none-any"]);
let markers = author.settings["runtimes"]["pip"]["python_markers"].clone();
set_manifest(&mut author, "pip", |manifest| {
manifest["python_markers"] = markers;
manifest["python_wheel_tags"] = json!(["py3-none-any"]);
manifest["python_extension_suffixes"] = json!([".abi3.so", ".so"]);
});
author
}
#[test]
fn ecosystem_archives_are_deterministic_and_signed_with_computed_identities() {
let mut author = runtime_author();
author.save();
let first = author.output("first");
let manifest = packaging::create(&author.root, &first).unwrap();
// Input order and filesystem mode are not archive metadata.
for ecosystem in ["pip", "npm", "go"] {
author.settings["runtimes"][ecosystem]["files"]
.as_array_mut()
.unwrap()
.reverse();
}
fs::set_permissions(
author.root.join("inputs/npm/bin/node"),
fs::Permissions::from_mode(0o700),
)
.unwrap();
author.save();
let second = author.output("second");
assert_eq!(packaging::create(&author.root, &second).unwrap(), manifest);
assert_eq!(contents(&first), contents(&second));
assert_signed(&author, &first);
for ecosystem in ["pip", "npm", "go"] {
let prefix = format!("runtime/{ecosystem}/{}", platform_identity());
let metadata_path = format!("{prefix}/manifest.json");
let pack_path = format!("{prefix}/runtime.pack");
let metadata: Value =
serde_json::from_slice(&fs::read(first.join(&metadata_path)).unwrap()).unwrap();
assert_eq!(metadata["format"], "dever-registry-runtime-v1");
assert_eq!(
metadata["runtime"]["pack"]["sha256"],
sha256_file(&first.join(&pack_path)).unwrap()
);
for path in [&metadata_path, &pack_path] {
let signed = manifest
.artifacts
.iter()
.find(|artifact| &artifact.path == path)
.unwrap();
assert_eq!(signed.sha256, sha256_file(&first.join(path)).unwrap());
}
let bytes = fs::read(first.join(&pack_path)).unwrap();
let mut archive = tar::Archive::new(flate2::read::GzDecoder::new(bytes.as_slice()));
let mut names = Vec::new();
for entry in archive.entries().unwrap() {
let mut entry = entry.unwrap();
let path = entry.path().unwrap().to_str().unwrap().to_owned();
assert_eq!(entry.header().mtime().unwrap(), 0);
assert_eq!(entry.header().uid().unwrap(), 0);
assert_eq!(entry.header().gid().unwrap(), 0);
assert_eq!(
entry.header().mode().unwrap(),
if path.starts_with("bin/") {
0o755
} else {
0o644
}
);
let mut actual = Vec::new();
entry.read_to_end(&mut actual).unwrap();
assert_eq!(
actual,
fs::read(author.root.join(format!("inputs/{ecosystem}/{path}"))).unwrap()
);
names.push(path);
}
assert!(names.windows(2).all(|pair| pair[0] < pair[1]));
}
let before = contents(&first);
fs::write(author.root.join("inputs/npm/bin/node"), b"changed").unwrap();
assert_eq!(contents(&first), before);
}
#[test]
fn rejects_incomplete_or_mismatched_runtime_descriptors() {
for ecosystem in ["pip", "npm", "go"] {
for field in ["format", "ecosystem", "target"] {
let mut author = runtime_author();
set_manifest(&mut author, ecosystem, |manifest| {
manifest[field] = json!("wrong")
});
assert!(author.reject().contains("identity"));
}
let mut author = runtime_author();
set_manifest(&mut author, ecosystem, |manifest| {
manifest["unknown"] = json!(true)
});
assert!(author.reject().contains("manifest"));
let mut author = runtime_author();
author.settings["runtimes"][ecosystem]["files"]
.as_array_mut()
.unwrap()
.retain(|input| input["path"] != "dever-runtime.json");
assert!(author.reject().contains("dever-runtime.json"));
}
for (ecosystem, field, value) in [
("pip", "arguments", json!(["-I", "-B"])),
("npm", "executable", json!("bin/missing")),
("npm", "arguments", json!(["source.js"])),
("go", "executable", json!("bin/compile")),
("go", "build", Value::Null),
] {
let mut author = runtime_author();
set_manifest(&mut author, ecosystem, |manifest| manifest[field] = value);
author.reject();
}
for (field, value) in [
("goarch", "unknown"),
("compiler", "bin/missing"),
("stdlib_importcfg", "missing"),
] {
let mut author = runtime_author();
set_manifest(&mut author, "go", |manifest| {
manifest["build"][field] = json!(value)
});
author.reject();
}
let mut author = runtime_author();
author.settings["runtimes"]["go"]["files"]
.as_array_mut()
.unwrap()
.retain(|input| input["path"] != "stdlib/context.a");
assert!(author.reject().contains("archive"));
}
#[test]
fn go_cross_pack_binds_host_tools_and_arm_standard_library_separately() {
for wrong_stdlib in [false, true] {
let mut author = runtime_author();
set_manifest(&mut author, "go", |manifest| {
manifest["target"] = json!("linux-aarch64");
manifest["build"]["goarch"] = json!("arm64");
});
for input in author.settings["runtimes"]["go"]["files"]
.as_array_mut()
.unwrap()
{
if !input["path"].as_str().unwrap().ends_with(".a") {
continue;
}
let path = author.root.join(input["source"].as_str().unwrap());
let architecture = if wrong_stdlib { "amd64" } else { "arm64" };
let object = format!("go object linux {architecture} go1.25.0 fixture\n");
let archive = format!(
"!<arch>\n{:<16}{:<12}{:<6}{:<6}{:<8}{:<10}`\n{object}",
"__.PKGDEF",
0,
0,
0,
"644",
object.len()
);
fs::write(&path, archive).unwrap();
input["sha256"] = json!(sha256_file(&path).unwrap());
}
let input = author.settings["runtimes"]
.as_object_mut()
.unwrap()
.remove("go")
.unwrap();
author.settings["ecosystem_targets"] = json!({"linux-aarch64":{"go":input}});
author.save();
let result = packaging::create(&author.root, &author.output("cross-go"));
if wrong_stdlib {
assert!(
result
.unwrap_err()
.contains("differs from its target or compiler version")
);
} else {
let manifest = result.unwrap();
assert!(
manifest
.artifacts
.iter()
.any(|file| file.path == "runtime/go/linux-aarch64/runtime.pack")
);
}
}
}
#[test]
fn sandbox_cross_assets_preserve_loader_mode_and_validate_deployment_target() {
let mut files = vec![
("bin/bwrap".into(), native_elf::static_executable()),
("bin/guard".into(), native_elf::static_executable()),
(
"lib/ld-linux-aarch64.so.1".into(),
native_elf::library("ld-linux-aarch64.so.1", &[]),
),
(
"lib/libc.so.6".into(),
native_elf::library("libc.so.6", &[]),
),
];
for (_, bytes) in &mut files {
bytes[18..20].copy_from_slice(&183u16.to_le_bytes());
}
dever_sandbox::validate_assets_for_target(&files, "linux-aarch64").unwrap();
let mut author = Author::new();
let inputs = files
.iter()
.map(|(path, bytes)| {
let source = format!("inputs/arm-sandbox/{path}");
let file = author.root.join(&source);
fs::create_dir_all(file.parent().unwrap()).unwrap();
fs::write(&file, bytes).unwrap();
fs::set_permissions(&file, fs::Permissions::from_mode(0o600)).unwrap();
json!({"source":source,"path":path,"sha256":sha256_file(&file).unwrap()})
})
.collect::<Vec<_>>();
author.settings["sandbox_targets"] = json!({"linux-aarch64":inputs});
author.save();
let output = author.output("cross-sandbox");
packaging::create(&author.root, &output).unwrap();
for (path, mode) in [
("bin/bwrap", 0o755),
("bin/guard", 0o755),
("lib/ld-linux-aarch64.so.1", 0o755),
("lib/libc.so.6", 0o644),
] {
assert_eq!(
fs::metadata(output.join("sandbox/linux-aarch64").join(path))
.unwrap()
.permissions()
.mode()
& 0o777,
mode,
"incorrect packaged mode for {path}"
);
}
files[1].1[18..20].copy_from_slice(&62u16.to_le_bytes());
assert!(
dever_sandbox::validate_assets_for_target(&files, "linux-aarch64")
.unwrap_err()
.contains("incompatible target")
);
}
#[test]
fn rejects_invalid_registry_metadata_and_unknown_ecosystems() {
for (ecosystem, field, value) in [
("pip", "python_markers", Value::Null),
("pip", "python_wheel_tags", json!([])),
("pip", "python_wheel_tags", json!(["invalid"])),
(
"pip",
"python_wheel_tags",
json!(["py3-none-any", "py3-none-any"]),
),
("npm", "python_wheel_tags", json!(["py3-none-any"])),
("npm", "name", json!("")),
("npm", "name", json!("../node")),
("go", "version", json!("latest")),
("go", "unknown", json!(true)),
] {
let mut author = runtime_author();
author.settings["runtimes"][ecosystem][field] = value;
author.reject();
}
let mut author = runtime_author();
author.settings["runtimes"]["npm"]["python_markers"] =
author.settings["runtimes"]["pip"]["python_markers"].clone();
author.reject();
let mut author = runtime_author();
author.settings["runtimes"]["exec"] = author.settings["runtimes"]["npm"].clone();
author.reject();
let author = runtime_author();
author.save();
let path = author.root.join("config/setting.json");
let config = fs::read_to_string(&path).unwrap();
let duplicate = config.replacen("\"runtimes\":{", "\"runtimes\":{\"npm\":null,", 1);
assert_ne!(duplicate, config);
fs::write(path, duplicate).unwrap();
assert!(
packaging::create(&author.root, &author.output("duplicate"))
.unwrap_err()
.contains("invalid author configuration")
);
assert_eq!(fs::read_dir(author.directory.path()).unwrap().count(), 1);
}
#[test]
fn rejects_runtime_path_conflicts_drift_and_private_key_aliases() {
for path in [
"../outside",
"bin",
"bin/node/child",
"runtime.pack",
"bin/node",
] {
let mut author = runtime_author();
let mut input = author.settings["runtimes"]["npm"]["files"][0].clone();
input["path"] = json!(path);
author.settings["runtimes"]["npm"]["files"]
.as_array_mut()
.unwrap()
.push(input);
author.reject();
}
let author = runtime_author();
fs::write(author.root.join("inputs/npm/bin/node"), b"changed").unwrap();
assert!(author.reject().contains("SHA-256"));
let mut author = self::runtime_author();
let alias = author.root.join("inputs/key-alias");
fs::hard_link(author.root.join("signing.pk8"), &alias).unwrap();
author.settings["runtimes"]["npm"]["files"].as_array_mut().unwrap().push(json!({"source":"inputs/key-alias","path":"secrets/key","sha256":sha256_file(&alias).unwrap()}));
assert!(author.reject().contains("cannot be included"));
let mut author = self::runtime_author();
symlink("node", author.root.join("inputs/npm/bin/link")).unwrap();
author.settings["runtimes"]["npm"]["files"][0]["source"] = json!("inputs/npm/bin/link");
author.reject();
}
@@ -0,0 +1,178 @@
use super::*;
use dever_cli::toolchain::runtime_pack::RuntimePack;
use dever_runtime::config::RuntimeProfile;
#[cfg(target_arch = "x86_64")]
fn cross_author() -> Author {
let mut author = Author::new();
let target = BuildTarget::LinuxAarch64;
let input = |name: &str, bytes: Vec<u8>| {
let source = format!("inputs/arm-{name}");
fs::write(author.root.join(&source), bytes).unwrap();
json!({"source":source, "sha256":sha256_file(&author.root.join(source)).unwrap()})
};
let mut profiles = json!({});
for profile in ["base", "sqlite", "postgres", "both"] {
profiles[profile] = input(profile, native_objects::archive(target, profile));
}
let link = |name: &str, bytes| {
let mut artifact = input(name, bytes);
artifact["path"] = json!(name);
artifact
};
author.settings["native_targets"] = json!({"linux-aarch64": {
"profiles":profiles,
"start":[link("start.o", native_objects::object(target))],
"libraries":[link("system.a", native_objects::archive(target, "system.o"))],
"end":[link("end.o", native_objects::object(target))],
}});
author
}
#[test]
fn target_names_and_serde_are_exact() {
for (name, target, triple) in [
(
"linux-x86_64",
BuildTarget::LinuxX86_64,
"x86_64-unknown-linux-gnu",
),
(
"linux-aarch64",
BuildTarget::LinuxAarch64,
"aarch64-unknown-linux-gnu",
),
] {
assert_eq!(name.parse::<BuildTarget>().unwrap(), target);
assert_eq!(target.platform(), name);
assert_eq!(target.triple(), triple);
assert_eq!(
serde_json::to_string(&target).unwrap(),
format!("\"{name}\"")
);
}
for invalid in [
"",
"arm64",
"Linux-aarch64",
"linux-arm64",
"aarch64-unknown-linux-gnu",
"linux-aarch64 ",
] {
assert!(invalid.parse::<BuildTarget>().is_err());
assert!(serde_json::from_value::<BuildTarget>(json!(invalid)).is_err());
}
}
#[test]
#[cfg(target_arch = "x86_64")]
fn host_core_packages_both_native_targets_with_separate_profiles_and_signed_closures() {
let author = cross_author();
author.save();
let output = author.directory.path().join("cross-release");
let manifest = packaging::create(&author.root, &output).unwrap();
assert_eq!(manifest.platform, "linux-x86_64");
assert_signed(&author, &output);
for target in [BuildTarget::LinuxX86_64, BuildTarget::LinuxAarch64] {
let closure = runtime_pack::validate(&output, env!("CARGO_PKG_VERSION"), target).unwrap();
for input in closure {
assert!(
manifest
.artifacts
.iter()
.any(|artifact| artifact.path == input.path
&& artifact.sha256 == input.sha256
&& artifact.bytes == input.bytes)
);
}
for profile in [
RuntimeProfile {
sqlite: false,
postgres: false,
},
RuntimeProfile {
sqlite: true,
postgres: false,
},
RuntimeProfile {
sqlite: false,
postgres: true,
},
RuntimeProfile {
sqlite: true,
postgres: true,
},
] {
assert_eq!(
RuntimePack::load(&output, profile, target).unwrap().target,
target
);
}
}
let profile = RuntimeProfile {
sqlite: false,
postgres: false,
};
let arm = RuntimePack::load(&output, profile, BuildTarget::LinuxAarch64).unwrap();
let host = RuntimePack::load(&output, profile, BuildTarget::LinuxX86_64).unwrap();
assert_ne!(arm.identity, host.identity);
fs::write(
output.join("runtime/linux-aarch64/archives/base.a"),
b"tampered",
)
.unwrap();
assert!(RuntimePack::load(&output, profile, BuildTarget::LinuxAarch64).is_err());
RuntimePack::load(&output, profile, BuildTarget::LinuxX86_64).unwrap();
}
#[test]
#[cfg(target_arch = "x86_64")]
fn target_packs_reject_wrong_architecture_and_duplicate_host_declarations() {
for (slot, source) in [
("/native_targets/linux-aarch64/start/0", "start.o"),
("/native_targets/linux-aarch64/profiles/base", "base"),
] {
let mut author = cross_author();
let replacement = if source.ends_with(".o") {
native_objects::object(BuildTarget::LinuxX86_64)
} else {
native_objects::archive(BuildTarget::LinuxX86_64, "wrong.o")
};
fs::write(
author.root.join(format!("inputs/arm-{source}")),
replacement,
)
.unwrap();
author.settings.pointer_mut(slot).unwrap()["sha256"] =
json!(sha256_file(&author.root.join(format!("inputs/arm-{source}"))).unwrap());
assert!(
author
.reject()
.contains("relocatable ELF object for linux-aarch64")
);
}
let mut author = cross_author();
author.settings["native_targets"]["linux-x86_64"] =
author.settings["native_targets"]["linux-aarch64"].clone();
assert!(author.reject().contains("duplicate release destination"));
}
#[test]
#[cfg(target_arch = "x86_64")]
fn missing_target_pack_never_uses_host_inputs() {
let author = Author::new();
author.save();
let output = author.directory.path().join("host-release");
packaging::create(&author.root, &output).unwrap();
let error = RuntimePack::load(
&output,
RuntimeProfile {
sqlite: false,
postgres: false,
},
BuildTarget::LinuxAarch64,
)
.err()
.unwrap();
assert!(error.contains("linux-aarch64"), "{error}");
}