Built alone from an empty CARGO_HOME, its tests passed, and flowrsc checked, ran and exported a document from the conformance corpus; the export built alone. Co-Authored-By: Claude Opus 5.5 <[email protected]>
110 lines
4.7 KiB
Rust
110 lines
4.7 KiB
Rust
//! The first-party keys, and the content address of every registry package flowRS's hosts
|
|
//! carry, computed from the very files cargo compiles into them.
|
|
//!
|
|
//! The keys: `FIRST_PARTY` is the id of each public key file in `registry/keys/`, so a host
|
|
//! trusts exactly what the repository publishes. Replacing the key is replacing the file.
|
|
//!
|
|
//! The packages:
|
|
//!
|
|
//! A host admits a package it carries only when the index holds a signed record for the
|
|
//! exact hash of that package's tree. The hash is taken here, at build time, from the tree
|
|
//! on disk ([`archive::read_tree`], the same reading `flowrsc pkg pack` does), so it is the
|
|
//! hash of the code this build compiles: edit one byte of a carried package without
|
|
//! verifying it again and every host refuses it with a hash mismatch, which is the contract
|
|
//! working. Each manifest is read here too and handed to the crate as JSON, so the browser
|
|
//! needs no TOML reader.
|
|
|
|
#[path = "src/archive.rs"]
|
|
#[allow(dead_code)]
|
|
mod archive;
|
|
|
|
use std::path::Path;
|
|
|
|
/// The packages the hosts carry, relative to the repository root.
|
|
const CARRIED: &[&str] = &[
|
|
"crates/flowrs-node",
|
|
"packages/dialect-opentap",
|
|
"packages/dialect-teststand",
|
|
"crates/flowrs-neuroviz",
|
|
];
|
|
|
|
/// The ids in `registry/keys/*.json`, in file order. A file whose id is not `v2:` and 64 hex
|
|
/// digits stops the build; the signature check itself holds the key to its id at run time.
|
|
fn first_party(root: &Path) -> Vec<String> {
|
|
let dir = root.join("registry/keys");
|
|
println!("cargo:rerun-if-changed={}", dir.display());
|
|
let mut files: Vec<_> = std::fs::read_dir(&dir)
|
|
.unwrap_or_else(|e| panic!("{}: {e}", dir.display()))
|
|
.flatten()
|
|
.map(|e| e.path())
|
|
.filter(|p| p.extension().is_some_and(|x| x == "json"))
|
|
.collect();
|
|
files.sort();
|
|
files
|
|
.iter()
|
|
.map(|p| {
|
|
let v: serde_json::Value = serde_json::from_str(
|
|
&std::fs::read_to_string(p).unwrap_or_else(|e| panic!("{}: {e}", p.display())),
|
|
)
|
|
.unwrap_or_else(|e| panic!("{}: {e}", p.display()));
|
|
let id = v["id"].as_str().unwrap_or_default().to_string();
|
|
let hex = id.strip_prefix("v2:").unwrap_or_default();
|
|
assert!(
|
|
hex.len() == 64 && hex.bytes().all(|b| b.is_ascii_hexdigit()),
|
|
"{}: `{id}` is not a key id",
|
|
p.display()
|
|
);
|
|
id
|
|
})
|
|
.collect()
|
|
}
|
|
|
|
fn main() {
|
|
println!("cargo:rerun-if-changed=build.rs");
|
|
println!("cargo:rerun-if-changed=src/archive.rs");
|
|
let here = std::env::var("CARGO_MANIFEST_DIR").expect("cargo sets CARGO_MANIFEST_DIR");
|
|
let root = Path::new(&here)
|
|
.parent()
|
|
.and_then(Path::parent)
|
|
.expect("the crate is at <root>/crates/flowrs-registry");
|
|
let out_dir = std::env::var("OUT_DIR").expect("cargo sets OUT_DIR");
|
|
let ids = first_party(root);
|
|
let mut keys = String::from(
|
|
"/// The first-party keys: the id of each public key file in `registry/keys/`.\n\
|
|
pub const FIRST_PARTY: &[&str] = &[\n",
|
|
);
|
|
for id in &ids {
|
|
keys.push_str(&format!(" {id:?},\n"));
|
|
}
|
|
keys.push_str("];\n");
|
|
std::fs::write(Path::new(&out_dir).join("first_party.rs"), keys).expect("OUT_DIR is writable");
|
|
// Only `carried` reads the table below, and only a host that carries packages needs their
|
|
// trees to exist: the public engine, which has `flowrsc pkg` but carries none, builds
|
|
// without them.
|
|
if std::env::var_os("CARGO_FEATURE_CARRIED").is_none() {
|
|
return;
|
|
}
|
|
let mut out = String::from(
|
|
"/// Generated by build.rs: each carried package's directory, content address and\n\
|
|
/// manifest (as JSON).\n\
|
|
pub const CARRIED: &[Carried] = &[\n",
|
|
);
|
|
for rel in CARRIED.iter().copied() {
|
|
let dir = root.join(rel);
|
|
println!("cargo:rerun-if-changed={}", dir.display());
|
|
let entries = archive::read_tree(&dir).unwrap_or_else(|e| panic!("{rel}: {e}"));
|
|
let bytes = archive::encode(&entries).unwrap_or_else(|e| panic!("{rel}: {e}"));
|
|
let hash = archive::hash(&bytes);
|
|
let toml_text = std::fs::read_to_string(dir.join("flowrs-package.toml"))
|
|
.unwrap_or_else(|e| panic!("{rel}/flowrs-package.toml: {e}"));
|
|
let value: toml::Value =
|
|
toml::from_str(&toml_text).unwrap_or_else(|e| panic!("{rel}/flowrs-package.toml: {e}"));
|
|
let json = serde_json::to_string(&value).expect("a TOML value is JSON");
|
|
out.push_str(&format!(
|
|
" Carried {{ dir: {rel:?}, hash: {hash:?}, manifest: {json:?} }},\n"
|
|
));
|
|
}
|
|
out.push_str("];\n");
|
|
let dest = Path::new(&out_dir).join("carried.rs");
|
|
std::fs::write(&dest, out).expect("OUT_DIR is writable");
|
|
}
|