use std::collections::BTreeMap;
use std::sync::LazyLock;
use serde::Deserialize;
use super::facet::{Capability, FacetTerm, Operation};
pub fn archetype(name: &str) -> Option<&'static Capability> {
ARCHETYPES.get(name)
}
pub fn names() -> impl Iterator<Item = &'static str> {
ARCHETYPES.keys().map(String::as_str)
}
static ARCHETYPES: LazyLock<BTreeMap<String, Capability>> =
LazyLock::new(|| build_archetypes(include_str!("../../archetypes.toml")).expect("embedded archetypes.toml must compile"));
#[derive(Debug, Clone, Deserialize)]
struct TomlDistinction {
#[allow(dead_code)] archetype: String,
#[allow(dead_code)] by: String,
}
#[cfg(test)]
type DeclaredDistinction = (String, Vec<(String, String)>, Option<String>);
#[cfg(test)]
fn declared_distinctions() -> Vec<DeclaredDistinction> {
let set: TomlArchetypeSet = toml::from_str(include_str!("../../archetypes.toml")).expect("archetypes.toml parses");
set.archetype
.into_iter()
.map(|(name, tc)| {
let d = tc.distinguished_from.into_iter().map(|x| (x.archetype, x.by)).collect();
(name, d, tc.same_point_as)
})
.collect()
}
fn build_archetypes(src: &str) -> Result<BTreeMap<String, Capability>, String> {
let set: TomlArchetypeSet = toml::from_str(src).map_err(|e| e.to_string())?;
set.archetype.into_iter().map(|(name, tc)| build_capability(&name, tc).map(|c| (name, c))).collect()
}
fn build_capability(name: &str, tc: TomlCapability) -> Result<Capability, String> {
let operation =
Operation::from_term(&tc.operation).ok_or_else(|| format!("archetype `{name}`: unknown operation `{}`", tc.operation))?;
let mut c = Capability::new(operation);
if let Some(l) = &tc.locus {
set_term(name, "locus.local", l.local.as_deref(), &mut c.locus.local)?;
set_term(name, "locus.remote", l.remote.as_deref(), &mut c.locus.remote)?;
set_term(name, "locus.binding", l.binding.as_deref(), &mut c.locus.binding)?;
set_term(name, "locus.provenance", l.provenance.as_deref(), &mut c.locus.provenance)?;
}
set_term(name, "scale", tc.scale.as_deref(), &mut c.scale)?;
set_term(name, "retrieval", tc.retrieval.as_deref(), &mut c.retrieval)?;
set_term(name, "authority", tc.authority.as_deref(), &mut c.authority)?;
set_term(name, "reversibility", tc.reversibility.as_deref(), &mut c.reversibility)?;
if let Some(p) = &tc.persistence {
set_term(name, "persistence.level", p.level.as_deref(), &mut c.persistence.level)?;
}
if let Some(d) = &tc.disclosure {
set_term(name, "disclosure.audience", d.audience.as_deref(), &mut c.disclosure.audience)?;
}
if let Some(s) = &tc.secret {
set_term(name, "secret.level", s.level.as_deref(), &mut c.secret.level)?;
}
if let Some(net) = &tc.network {
set_term(name, "network.direction", net.direction.as_deref(), &mut c.network.direction)?;
set_term(name, "network.destination", net.destination.as_deref(), &mut c.network.destination)?;
set_term(name, "network.payload", net.payload.as_deref(), &mut c.network.payload)?;
}
set_term(name, "execution", tc.execution.as_deref(), &mut c.execution.trust)?;
set_term(name, "cost", tc.cost.as_deref(), &mut c.cost)?;
if tc.because.trim().is_empty() {
return Err(format!("archetype `{name}`: `because` is required"));
}
c.because = tc.because;
Ok(c)
}
fn set_term<T: FacetTerm>(name: &str, field: &str, s: Option<&str>, slot: &mut T) -> Result<(), String> {
if let Some(v) = s {
*slot = T::from_term(v).ok_or_else(|| format!("archetype `{name}`: unknown {field} term `{v}`"))?;
}
Ok(())
}
#[derive(Deserialize)]
struct TomlArchetypeSet {
#[serde(default)]
archetype: BTreeMap<String, TomlCapability>,
}
#[derive(Deserialize)]
#[serde(deny_unknown_fields)]
struct TomlCapability {
operation: String,
because: String,
#[serde(default)]
#[allow(dead_code)] distinguished_from: Vec<TomlDistinction>,
#[serde(default)]
#[allow(dead_code)] same_point_as: Option<String>,
#[serde(default)]
locus: Option<TomlLocus>,
#[serde(default)]
scale: Option<String>,
#[serde(default)]
retrieval: Option<String>,
#[serde(default)]
authority: Option<String>,
#[serde(default)]
reversibility: Option<String>,
#[serde(default)]
persistence: Option<TomlPersistence>,
#[serde(default)]
disclosure: Option<TomlDisclosure>,
#[serde(default)]
secret: Option<TomlSecret>,
#[serde(default)]
network: Option<TomlNetwork>,
#[serde(default)]
execution: Option<String>,
#[serde(default)]
cost: Option<String>,
}
#[derive(Deserialize)]
#[serde(deny_unknown_fields)]
struct TomlLocus {
local: Option<String>,
remote: Option<String>,
binding: Option<String>,
provenance: Option<String>,
}
#[derive(Deserialize)]
#[serde(deny_unknown_fields)]
struct TomlPersistence {
level: Option<String>,
}
#[derive(Deserialize)]
#[serde(deny_unknown_fields)]
struct TomlDisclosure {
audience: Option<String>,
}
#[derive(Deserialize)]
#[serde(deny_unknown_fields)]
struct TomlSecret {
level: Option<String>,
}
#[derive(Deserialize)]
#[serde(deny_unknown_fields)]
struct TomlNetwork {
direction: Option<String>,
destination: Option<String>,
payload: Option<String>,
}
#[cfg(test)]
mod tests {
use super::*;
use crate::engine::authoring::default_levels;
use crate::engine::bridge::project;
use crate::engine::facet::Profile;
use crate::engine::level::Level;
use crate::verdict::Verdict;
fn level(name: &str) -> &'static Level {
default_levels().iter().find(|l| l.name == name).expect("level exists")
}
#[test]
fn sibling_adjacent_locus_reads_at_reader_writes_at_developer_not_editor() {
use crate::engine::facet::{Capability, LocalLocus, Operation, PersistenceLevel, Reversibility};
let at_adjacent = |op: Operation| {
let mut c = Capability::new(op);
c.locus.local = LocalLocus::Adjacent;
c
};
let read = Profile::of(vec![at_adjacent(Operation::Observe)]);
let patch = Profile::of(vec![{
let mut c = at_adjacent(Operation::Mutate);
c.reversibility = Reversibility::Recoverable;
c.persistence.level = PersistenceLevel::Data;
c
}]);
let destroy = Profile::of(vec![at_adjacent(Operation::Destroy)]);
assert!(level("reader").admits(&read), "a sibling READ auto-approves from reader");
assert!(level("developer").admits(&read), "…and higher");
assert!(!level("editor").admits(&patch), "editor does NOT write a sibling (writes stay worktree)");
assert!(level("developer").admits(&patch), "developer patches a sibling (create/mutate)");
assert!(!level("editor").admits(&destroy), "editor does NOT destroy a sibling");
assert!(
!level("developer").admits(&destroy),
"developer does NOT destroy a sibling (conservative — its destroy clause stays `<= worktree`)"
);
}
#[test]
fn archetypes_toml_compiles_and_every_capability_is_justified() {
let mut count = 0;
for n in names() {
let c = archetype(n).expect("listed archetype resolves");
assert!(!c.because.is_empty(), "archetype `{n}` has no because");
count += 1;
}
assert!(count >= 10, "expected the full catalog, got {count}");
assert!(archetype("does-not-exist").is_none(), "unknown profile fails closed");
}
#[test]
fn archetypes_land_where_the_catalog_says() {
let cases: &[(&str, &str, &str)] = &[
("remote-read", "reader", "paranoid"),
("data-export", "reader", "paranoid"),
("remote-mutate", "network-admin", "developer"),
("remote-create", "network-admin", "developer"),
("remote-destroy-recoverable", "network-admin", "developer"),
("remote-destroy-irreversible", "yolo", "network-admin"),
("remote-authorize", "network-admin", "developer"),
("remote-control", "network-admin", "developer"),
("vcs-sync", "network-admin", "developer"),
("blockchain-txn", "yolo", "network-admin"),
("local-privileged", "local-admin", "developer"),
("privileged-control", "local-admin", "developer"),
("local-install-pinned", "developer", "editor"),
("supply-chain-build", "yolo", "developer"),
("remote-exec", "yolo", "network-admin"),
("credential-read", "yolo", "network-admin"),
("credential-mint", "yolo", "network-admin"),
("decrypt-read", "yolo", "network-admin"),
("bulk-object-read", "network-admin", "developer"),
("local-mutate-trivial", "editor", "reader"),
("local-mutate-recoverable", "editor", "reader"),
("local-destroy-recoverable", "developer", "editor"),
("local-destroy-irreversible", "yolo", "developer"),
];
for (name, admitted_by, refused_by) in cases {
let p = Profile::of(vec![archetype(name).expect("archetype exists").clone()]);
assert!(level(admitted_by).admits(&p), "{name} should be admitted by {admitted_by}");
assert!(!level(refused_by).admits(&p), "{name} should be refused by {refused_by}");
}
let uncovered: Vec<&str> = names().filter(|n| !cases.iter().any(|(c, _, _)| c == n)).collect();
assert!(uncovered.is_empty(), "archetype(s) with no catalog row — add (name, admitted_by, refused_by) above: {uncovered:?}",);
}
#[test]
fn every_remote_write_archetype_is_not_auto_approved() {
let write_remotes =
names().filter(|n| (n.starts_with("remote-") && *n != "remote-read") || *n == "vcs-sync" || *n == "blockchain-txn");
for name in write_remotes {
let p = Profile::of(vec![archetype(name).expect("archetype").clone()]);
assert_eq!(project(&p), Verdict::Denied, "{name} must not auto-approve in the 3-value projection");
}
assert_eq!(
project(&Profile::of(vec![archetype("remote-read").unwrap().clone()])),
Verdict::Allowed(crate::verdict::SafetyLevel::SafeRead),
"a pure remote fetch is reader-level",
);
}
#[test]
fn public_disclosure_is_recorded_not_gated_secret_transmission_is() {
use crate::engine::facet::{
DisclosureAudience, NetDestination, NetDirection, NetPayload, Network, RemoteReach, Reversibility, SecretLevel,
};
let publish_to_public = || {
let mut c = Capability::new(Operation::Communicate);
c.locus.remote = RemoteReach::Arbitrary;
c.reversibility = Reversibility::Effortful;
c.disclosure.audience = DisclosureAudience::Public;
c.network =
Network { direction: NetDirection::Outbound, destination: NetDestination::Arbitrary, payload: NetPayload::SendsHostData };
c
};
let mut publish = publish_to_public();
publish.because = "publish authored content to a public destination".into();
let publish = Profile::of(vec![publish]);
assert!(level("network-admin").admits(&publish), "public non-secret publish is network-admin");
assert!(!level("developer").admits(&publish), "outbound remote egress is above developer");
let mut exfil = publish_to_public();
exfil.secret.level = SecretLevel::Transmits;
exfil.because = "transmit a secret off-box".into();
let exfil = Profile::of(vec![exfil]);
assert!(!level("network-admin").admits(&exfil), "secret transmission is the gate, above network-admin");
assert!(level("yolo").admits(&exfil), "yolo admits secret exfil (non-destroy clause)");
}
#[test]
fn system_integrity_is_above_local_admin_ordinary_machine_is_not() {
use crate::engine::facet::{Authority, LocalLocus};
let (local, yolo) = (level("local-admin"), level("yolo"));
let root_write_at = |loc| {
let mut c = Capability::new(Operation::Mutate);
c.locus.local = loc;
c.authority = Authority::Root;
c.because = "root machine write".into();
Profile::of(vec![c])
};
assert!(local.admits(&root_write_at(LocalLocus::Machine)), "ordinary machine write is local-admin");
let integrity = root_write_at(LocalLocus::SystemIntegrity);
assert!(!local.admits(&integrity), "the system-integrity substrate is above local-admin");
assert!(yolo.admits(&integrity), "yolo owns the machine's trust root");
}
#[test]
fn pinned_scripts_off_install_is_developer_the_supply_chain_surface_is_yolo() {
use crate::engine::facet::{ExecutionTrust, LocalLocus, NetDirection, NetPayload, PersistenceLevel, Reversibility};
let (dev, yolo) = (level("developer"), level("yolo"));
let safe_install = {
let mut c = Capability::new(Operation::Create);
c.locus.local = LocalLocus::Worktree;
c.persistence.level = PersistenceLevel::Installing;
c.reversibility = Reversibility::Effortful;
c.network.direction = NetDirection::Outbound;
c.network.payload = NetPayload::Fetches;
c.execution.trust = ExecutionTrust::SelfCode;
c.because = "pinned, scripts-off install".into();
Profile::of(vec![c])
};
assert!(dev.admits(&safe_install), "a pinned, scripts-off install is developer");
assert!(yolo.admits(&safe_install), "and of course yolo");
let supply_chain = Profile::of(vec![archetype("supply-chain-build").unwrap().clone()]);
assert!(!dev.admits(&supply_chain), "network-sourced install (scripts on / unpinned) is above developer");
assert!(yolo.admits(&supply_chain), "the supply-chain surface lands at yolo");
}
#[test]
fn a_literal_send_target_is_network_admin_an_opaque_one_is_yolo() {
use crate::engine::facet::{NetDirection, NetPayload, Provenance, RemoteReach};
let send_to = |prov| {
let mut c = Capability::new(Operation::Communicate);
c.locus.remote = RemoteReach::Fixed;
c.locus.provenance = prov;
c.network.direction = NetDirection::Outbound;
c.network.payload = NetPayload::SendsHostData;
c.because = "send host data to a designated target".into();
c
};
let literal = Profile::of(vec![send_to(Provenance::Literal)]);
assert!(level("network-admin").admits(&literal), "a visible literal URL is a network-admin send");
assert!(!level("developer").admits(&literal), "sends-host-data is above the local developer band");
let opaque = Profile::of(vec![send_to(Provenance::Opaque)]);
assert!(!level("network-admin").admits(&opaque), "an opaque (variable) destination is held above network-admin");
assert!(level("yolo").admits(&opaque), "yolo leaves provenance unconstrained");
}
}
#[cfg(test)]
mod neighbour_tests {
use super::*;
use std::collections::BTreeMap;
fn differing_facets(a: &Capability, b: &Capability) -> Vec<&'static str> {
let am: BTreeMap<_, _> = a.set_facets().into_iter().collect();
let bm: BTreeMap<_, _> = b.set_facets().into_iter().collect();
let mut keys: Vec<_> = am.keys().chain(bm.keys()).copied().collect();
keys.sort_unstable();
keys.dedup();
keys.into_iter().filter(|k| am.get(k) != bm.get(k)).collect()
}
#[test]
fn near_neighbours_are_declared() {
let declared = declared_distinctions();
let dist_of = |n: &str| -> Vec<(String, String)> {
declared.iter().find(|(name, ..)| name == n).map(|(_, d, _)| d.clone()).unwrap_or_default()
};
let same_of = |n: &str| -> Option<String> { declared.iter().find(|(name, ..)| name == n).and_then(|(_, _, s)| s.clone()) };
let names: Vec<&str> = names().collect();
let mut problems = Vec::new();
for (i, a) in names.iter().enumerate() {
for b in &names[i + 1..] {
let d = differing_facets(archetype(a).unwrap(), archetype(b).unwrap());
match d.len() {
0 if same_of(a).as_deref() != Some(*b) || same_of(b).as_deref() != Some(*a) => {
problems.push(format!(
"`{a}` and `{b}` are facet-IDENTICAL; both must declare \
`same_point_as` naming the other, or be given a real difference",
));
}
1 => {
let axis = d[0];
for (x, y) in [(a, b), (b, a)] {
if !dist_of(x).iter().any(|(n, by)| n == *y && by == axis) {
problems.push(format!(
"`{x}` must declare `distinguished_from = [{{ archetype = \"{y}\", \
by = \"{axis}\" }}]` — they differ on that axis alone",
));
}
}
}
_ => {}
}
}
}
assert!(problems.is_empty(), "confusable archetypes:\n {}", problems.join("\n "));
}
#[test]
fn declared_distinctions_are_accurate() {
let mut problems = Vec::new();
for (name, dists, same) in declared_distinctions() {
let Some(a) = archetype(&name) else { continue };
for (other, by) in dists {
let Some(b) = archetype(&other) else {
problems.push(format!("`{name}` names unknown archetype `{other}`"));
continue;
};
let d = differing_facets(a, b);
if !d.contains(&by.as_str()) {
problems.push(format!("`{name}` claims it differs from `{other}` by `{by}`, but they differ on {d:?}",));
}
}
if let Some(other) = same {
match archetype(&other) {
None => problems.push(format!("`{name}` names unknown archetype `{other}`")),
Some(b) => {
let d = differing_facets(a, b);
if !d.is_empty() {
problems.push(format!("`{name}` claims `same_point_as = \"{other}\"`, but they differ on {d:?}",));
}
}
}
}
}
assert!(problems.is_empty(), "inaccurate distinctions:\n {}", problems.join("\n "));
}
}