diff --git a/Cargo.lock b/Cargo.lock index 5bcf13b..4cca67f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -44,14 +44,15 @@ dependencies = [ "indicatif", "jiff", "kerykeion", - "koinon", "kryphos", "rpassword", "serde", "serde_json", "snafu", + "stoicheion", "syn 3.0.3", "syntonia", + "tekmerion", "tempfile", "tokio", "tracing", @@ -1401,7 +1402,6 @@ dependencies = [ "ctr", "futures", "jiff", - "koinon", "petgraph", "proptest", "prost", @@ -1410,6 +1410,7 @@ dependencies = [ "serde", "serde_json", "snafu", + "stoicheion", "tokio", "tokio-serial", "tokio-util", @@ -1417,27 +1418,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "koinon" -version = "0.6.1" -dependencies = [ - "blake3", - "ciborium", - "compact_str", - "fs2", - "jiff", - "proptest", - "serde", - "serde_json", - "snafu", - "subtle", - "tempfile", - "toml 1.1.4+spec-1.1.0", - "tracing", - "ulid", - "zeroize", -] - [[package]] name = "kryphos" version = "0.6.1" @@ -1451,12 +1431,12 @@ dependencies = [ "fjall", "fs2", "jiff", - "koinon", "rand_core 0.6.4", "serde", "serde_json", "snafu", "subtle", + "tekmerion", "tempfile", "zeroize", ] @@ -2169,9 +2149,9 @@ name = "semaino" version = "0.6.1" dependencies = [ "jiff", - "koinon", "serde", "snafu", + "stoicheion", "tokio", "toml 1.1.4+spec-1.1.0", "tracing", @@ -2417,6 +2397,20 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "stoicheion" +version = "0.6.1" +dependencies = [ + "compact_str", + "jiff", + "proptest", + "serde", + "serde_json", + "snafu", + "tracing", + "ulid", +] + [[package]] name = "strsim" version = "0.11.1" @@ -2463,16 +2457,37 @@ version = "0.6.1" dependencies = [ "compact_str", "csv", - "koinon", "proptest", "rusb", "serde", "serde_json", "serialport", "snafu", + "stoicheion", + "tempfile", + "toml 1.1.4+spec-1.1.0", + "tracing", +] + +[[package]] +name = "tekmerion" +version = "0.6.1" +dependencies = [ + "blake3", + "ciborium", + "compact_str", + "fs2", + "jiff", + "serde", + "serde_json", + "snafu", + "stoicheion", + "subtle", "tempfile", "toml 1.1.4+spec-1.1.0", "tracing", + "ulid", + "zeroize", ] [[package]] diff --git a/crates/akroasis/Cargo.toml b/crates/akroasis/Cargo.toml index 62d21ef..8886dcd 100644 --- a/crates/akroasis/Cargo.toml +++ b/crates/akroasis/Cargo.toml @@ -29,7 +29,8 @@ figment = { workspace = true } indicatif = "0.17" jiff = { workspace = true } kerykeion = { path = "../kerykeion" } -koinon = { path = "../koinon" } +stoicheion = { path = "../stoicheion" } +tekmerion = { path = "../tekmerion" } kryphos = { path = "../kryphos" } rpassword = { workspace = true } serde = { workspace = true } diff --git a/crates/akroasis/src/caller.rs b/crates/akroasis/src/caller.rs index 94c47ac..ebed9a0 100644 --- a/crates/akroasis/src/caller.rs +++ b/crates/akroasis/src/caller.rs @@ -6,14 +6,15 @@ use std::fmt; -use koinon::{ +use snafu::{ResultExt, Snafu}; +use stoicheion::Timestamp; +use tekmerion::{ AuthorizationDenial, AuthorizationRequirement, CALLER_RESOLVER_VERSION, CallerAuthority, CallerContractError, CallerRef, CallerResolver, EffectDescriptor, EffectOutcome, EffectReceipt, EffectReceiptError, EffectReceiptSink, EvidenceDigest, LocalPeerCredentials, PersistedIntent, PersistedOutcome, ReceiptDigest, RecoveryAuthorization, RecoveryTicket, RevocationState, - Timestamp, ValidatedCaller, authorize_caller, + ValidatedCaller, authorize_caller, }; -use snafu::{ResultExt, Snafu}; use tokio::net::UnixStream; /// Version of accepted service-identity evidence. @@ -423,19 +424,19 @@ where const fn outcome_needs_recovery( outcome: EffectOutcome, - recovery: koinon::RecoveryRelation, + recovery: tekmerion::RecoveryRelation, ) -> bool { matches!( (outcome, recovery), ( EffectOutcome::Partial | EffectOutcome::RecoveryRequired, - koinon::RecoveryRelation::RequiredFor(_) + tekmerion::RecoveryRelation::RequiredFor(_) ) | ( EffectOutcome::Failed | EffectOutcome::Cancelled | EffectOutcome::Backpressured | EffectOutcome::AuthorizationDenied, - koinon::RecoveryRelation::RecoveryOf(_) + tekmerion::RecoveryRelation::RecoveryOf(_) ) ) } diff --git a/crates/akroasis/src/caller_tests.rs b/crates/akroasis/src/caller_tests.rs index 57275f0..be0ef0c 100644 --- a/crates/akroasis/src/caller_tests.rs +++ b/crates/akroasis/src/caller_tests.rs @@ -8,7 +8,7 @@ use std::cell::Cell; use std::rc::Rc; -use koinon::{ +use tekmerion::{ AuthorityClaims, AuthorityDecision, AuthorityGrant, AuthorizationDenial, AuthorizationRequirement, CALLER_CONTEXT_VERSION, CallerContractError, CapabilityRef, PersonaRef, PolicyEpoch, PrincipalSource, ScopeRef, authorize_caller, diff --git a/crates/akroasis/src/radio/export.rs b/crates/akroasis/src/radio/export.rs index 3d0b90b..b6ba497 100644 --- a/crates/akroasis/src/radio/export.rs +++ b/crates/akroasis/src/radio/export.rs @@ -278,7 +278,7 @@ fn csv_escape(s: &str) -> String { reason = "test code: panics and unwraps acceptable in assertions" )] mod tests { - use koinon::Frequency; + use stoicheion::Frequency; use syntonia::tone::CtcssTone; use syntonia::types::FrequencyOffset; diff --git a/crates/akroasis/src/radio/import.rs b/crates/akroasis/src/radio/import.rs index 521ec4a..bc3f342 100644 --- a/crates/akroasis/src/radio/import.rs +++ b/crates/akroasis/src/radio/import.rs @@ -3,9 +3,9 @@ use std::io::Write; use std::path::Path; -use koinon::Frequency; use serde::Serialize; use snafu::ResultExt; +use stoicheion::Frequency; use syntonia::{Bandwidth, Channel, FrequencyPlan, PowerLevel, ScanMode, ToneMode}; use super::errors::{IoSnafu, JsonReportSnafu, RadioError, ReadFileSnafu, SyntoniaSnafu}; diff --git a/crates/akroasis/src/radio/program.rs b/crates/akroasis/src/radio/program.rs index 388a88f..928d7dc 100644 --- a/crates/akroasis/src/radio/program.rs +++ b/crates/akroasis/src/radio/program.rs @@ -128,7 +128,7 @@ pub(crate) fn load_plan(path: &Path) -> Result { reason = "test code: panics and unwraps acceptable in assertions" )] mod tests { - use koinon::Frequency; + use stoicheion::Frequency; use syntonia::{Bandwidth, Channel, PowerLevel, ScanMode, ToneMode, types::FrequencyOffset}; use super::*; diff --git a/crates/akroasis/src/radio/read.rs b/crates/akroasis/src/radio/read.rs index 29f3e7d..2bcd754 100644 --- a/crates/akroasis/src/radio/read.rs +++ b/crates/akroasis/src/radio/read.rs @@ -129,7 +129,7 @@ const fn format_scan(scan: ScanMode) -> &'static str { reason = "test code: panics and unwraps acceptable in assertions" )] mod tests { - use koinon::Frequency; + use stoicheion::Frequency; use syntonia::tone::CtcssTone; use syntonia::types::FrequencyOffset; diff --git a/crates/akroasis/src/radio/serial_hardware.rs b/crates/akroasis/src/radio/serial_hardware.rs index 8566284..39e1758 100644 --- a/crates/akroasis/src/radio/serial_hardware.rs +++ b/crates/akroasis/src/radio/serial_hardware.rs @@ -1,6 +1,6 @@ //! Feature-gated live serial radio detection and protocol session backend. -use koinon::RadioKind; +use stoicheion::RadioKind; use syntonia::baofeng::{ codec::{CodecError, decode_all_channels, encode_all_channels}, image::MemoryImage, diff --git a/crates/akroasis/tests/caller_architecture.rs b/crates/akroasis/tests/caller_architecture.rs index 7c4bb01..2616350 100644 --- a/crates/akroasis/tests/caller_architecture.rs +++ b/crates/akroasis/tests/caller_architecture.rs @@ -90,11 +90,7 @@ fn restricted_identifiers(source: &str) -> BTreeSet { #[test] fn caller_construction_aliases_are_detected() { let source = r" - use koinon::{ - AuthorityClaimsBuilder as Claims, - CallerAuthority as Authority, - CallerResolver as Resolver, - }; + use tekmerion::{AuthorityClaimsBuilder as Claims, CallerAuthority as Authority, CallerResolver as Resolver}; fn bypass(authority: A) { let _claims = Claims::new(); @@ -166,8 +162,8 @@ fn is_allowed(path: &Path) -> bool { || path == Path::new("crates/akroasis/tests/caller_contract.rs") || path == Path::new("crates/akroasis/tests/caller_recovery.rs") || path == Path::new("crates/akroasis/tests/caller_receipt_wire.rs") - || path == Path::new("crates/koinon/src/caller.rs") - || path == Path::new("crates/koinon/src/lib.rs") + || path == Path::new("crates/tekmerion/src/caller.rs") + || path == Path::new("crates/tekmerion/src/lib.rs") } fn collect_rust_files(path: &Path, files: &mut Vec) { diff --git a/crates/akroasis/tests/caller_contract.rs b/crates/akroasis/tests/caller_contract.rs index 0bdccdb..a6d2430 100644 --- a/crates/akroasis/tests/caller_contract.rs +++ b/crates/akroasis/tests/caller_contract.rs @@ -13,14 +13,15 @@ use akroasis_lib::caller::{ AdmissionDecision, ApplicationCallerError, ApplicationCallerResolver, EffectExecution, EffectGateError, EffectRequest, TrustedClock, execute_effect, }; -use koinon::{ +use snafu::Snafu; +use stoicheion::Timestamp; +use tekmerion::{ AuthorityClaims, AuthorityDecision, AuthorityGrant, AuthorizationDenial, AuthorizationRequirement, CALLER_CONTEXT_VERSION, CALLER_RESOLVER_VERSION, CallerAuthority, CallerContractError, CallerRef, CapabilityRef, EffectDescriptor, EffectOutcome, EffectReceipt, EffectReceiptError, EffectReceiptSink, EffectRef, LocalPeerCredentials, PersonaRef, - PolicyEpoch, ReceiptDigest, ReceiptEvent, SchemaEpoch, ScopeRef, Timestamp, + PolicyEpoch, ReceiptDigest, ReceiptEvent, SchemaEpoch, ScopeRef, }; -use snafu::Snafu; use tokio::net::UnixStream; #[derive(Debug, Clone, Copy)] @@ -296,7 +297,7 @@ fn standard_grant() -> AuthorityGrant { ) } -fn valid_caller() -> koinon::ValidatedCaller { +fn valid_caller() -> tekmerion::ValidatedCaller { let authority = FakeAuthority::granted(standard_grant(), policy_epoch(7)); let resolver = ApplicationCallerResolver::current(authority).expect("current resolver"); let (client, _server) = UnixStream::pair().expect("Unix socket pair"); diff --git a/crates/akroasis/tests/caller_receipt_wire.rs b/crates/akroasis/tests/caller_receipt_wire.rs index cdbba64..ede009f 100644 --- a/crates/akroasis/tests/caller_receipt_wire.rs +++ b/crates/akroasis/tests/caller_receipt_wire.rs @@ -11,14 +11,14 @@ use akroasis_lib::caller::{ AdmissionDecision, ApplicationCallerResolver, EffectExecution, EffectRequest, TrustedClock, execute_effect, }; -use koinon::{ +use snafu::Snafu; +use stoicheion::Timestamp; +use tekmerion::{ AuthorityClaims, AuthorityDecision, AuthorityGrant, AuthorizationRequirement, CALLER_CONTEXT_VERSION, CallerAuthority, CallerRef, CapabilityRef, EffectDescriptor, EffectReceipt, EffectReceiptError, EffectReceiptSink, EffectRef, EvidenceDigest, LocalPeerCredentials, PolicyEpoch, ReceiptDigest, RecoveryRelation, SchemaEpoch, ScopeRef, - Timestamp, }; -use snafu::Snafu; use tokio::net::UnixStream; #[derive(Debug, Clone, Copy)] diff --git a/crates/akroasis/tests/caller_recovery.rs b/crates/akroasis/tests/caller_recovery.rs index 6a46eb6..040faa8 100644 --- a/crates/akroasis/tests/caller_recovery.rs +++ b/crates/akroasis/tests/caller_recovery.rs @@ -12,14 +12,15 @@ use akroasis_lib::caller::{ AdmissionDecision, ApplicationCallerResolver, EffectExecution, EffectGateError, EffectRequest, TrustedClock, execute_effect, }; -use koinon::{ +use snafu::Snafu; +use stoicheion::Timestamp; +use tekmerion::{ AuthorityClaims, AuthorityDecision, AuthorityGrant, AuthorizationRequirement, CALLER_CONTEXT_VERSION, CallerAuthority, CallerRef, CapabilityRef, EFFECT_RECEIPT_VERSION, EffectDescriptor, EffectOutcome, EffectReceipt, EffectReceiptError, EffectReceiptSink, EffectRef, LocalPeerCredentials, PersistedIntent, PersistedOutcome, PolicyEpoch, ReceiptDigest, - ReceiptEvent, RecoveryAuthorization, RecoveryRelation, SchemaEpoch, ScopeRef, Timestamp, + ReceiptEvent, RecoveryAuthorization, RecoveryRelation, SchemaEpoch, ScopeRef, }; -use snafu::Snafu; use tokio::net::UnixStream; #[derive(Debug, Clone, Copy)] @@ -255,7 +256,7 @@ fn grant() -> AuthorityGrant { ) } -fn caller() -> koinon::ValidatedCaller { +fn caller() -> tekmerion::ValidatedCaller { let resolver = ApplicationCallerResolver::current(RecoveryAuthority { grant: grant() }) .expect("current caller resolver"); let (client, _server) = UnixStream::pair().expect("Unix socket pair"); diff --git a/crates/kerykeion/Cargo.toml b/crates/kerykeion/Cargo.toml index 582cdc8..3a6fddc 100644 --- a/crates/kerykeion/Cargo.toml +++ b/crates/kerykeion/Cargo.toml @@ -7,7 +7,7 @@ license.workspace = true description = "Meshtastic mesh networking integration for Akroasis" [dependencies] -koinon = { path = "../koinon" } +stoicheion = { path = "../stoicheion" } serde = { workspace = true } serde_json = { workspace = true } snafu = { workspace = true } diff --git a/crates/kerykeion/src/collector.rs b/crates/kerykeion/src/collector.rs index b5ba288..13f3973 100644 --- a/crates/kerykeion/src/collector.rs +++ b/crates/kerykeion/src/collector.rs @@ -8,7 +8,7 @@ use std::sync::Arc; use std::time::Duration; -use koinon::GeoSignal; +use stoicheion::GeoSignal; use tokio::sync::{Mutex, broadcast}; use tokio::task::JoinSet; use tokio_util::sync::CancellationToken; @@ -37,7 +37,10 @@ use crate::types::ClaimedNodeNum; /// Trait for Akroasis data collectors. /// /// Defines the minimal lifecycle interface shared across all collector crates. -/// This is a local definition pending the addition of `koinon::Collector`. +/// This is a local definition pending a shared one. Which crate it belongs in +/// is genuinely open: a collector produces signals, which is `stoicheion`'s +/// layer, but it is a pipeline role rather than an element, so it may belong +/// in neither. Left local until something forces the choice. pub trait Collector: Send + Sync { /// Returns the canonical name of this collector. fn name(&self) -> &'static str; diff --git a/crates/kerykeion/src/discovery.rs b/crates/kerykeion/src/discovery.rs index b14d8aa..de2e6c8 100644 --- a/crates/kerykeion/src/discovery.rs +++ b/crates/kerykeion/src/discovery.rs @@ -2,7 +2,7 @@ use std::time::Duration; -use koinon::GeoSignal; +use stoicheion::GeoSignal; use tokio::sync::broadcast; use tokio_util::sync::CancellationToken; use tracing::instrument; diff --git a/crates/kerykeion/src/processor.rs b/crates/kerykeion/src/processor.rs index bbc9448..a72fcdf 100644 --- a/crates/kerykeion/src/processor.rs +++ b/crates/kerykeion/src/processor.rs @@ -1,7 +1,7 @@ //! Central packet dispatch for incoming `FromRadio` messages after handshake. -use koinon::GeoSignal; use prost::Message as _; +use stoicheion::GeoSignal; use tokio::sync::broadcast; use crate::delivery::{DeliveryFailure, DeliveryTracker}; @@ -301,7 +301,7 @@ impl PacketProcessor { // unchecked. `Coordinates::new` already owns this rule for the whole // fleet, so it is reused rather than restated; it also rejects NaN, // which a bare range comparison would silently admit. - if let Err(error) = koinon::Coordinates::new(lat, lon, None) { + if let Err(error) = stoicheion::Coordinates::new(lat, lon, None) { tracing::warn!( from = from.0, %error, diff --git a/crates/kerykeion/src/signals.rs b/crates/kerykeion/src/signals.rs index 7e4ff63..dac93a1 100644 --- a/crates/kerykeion/src/signals.rs +++ b/crates/kerykeion/src/signals.rs @@ -1,7 +1,7 @@ -//! Mesh event types and conversion to [`koinon::GeoSignal`] for the broadcast channel. +//! Mesh event types and conversion to [`stoicheion::GeoSignal`] for the broadcast channel. -use koinon::signal::MeshDetail; -use koinon::{Confidence, Coordinates, GeoSignal, SignalKind, Timestamp}; +use stoicheion::signal::MeshDetail; +use stoicheion::{Confidence, Coordinates, GeoSignal, SignalKind, Timestamp}; use crate::node_db::NodePosition; use crate::types::NodeNum; diff --git a/crates/kryphos/Cargo.toml b/crates/kryphos/Cargo.toml index 6f92f4c..9d332ca 100644 --- a/crates/kryphos/Cargo.toml +++ b/crates/kryphos/Cargo.toml @@ -23,7 +23,7 @@ compact_str.workspace = true figment.workspace = true fjall.workspace = true fs2.workspace = true -koinon = { path = "../koinon" } +tekmerion = { path = "../tekmerion" } [dev-dependencies] serde_json.workspace = true diff --git a/crates/kryphos/src/error.rs b/crates/kryphos/src/error.rs index 570e5b6..bcefc89 100644 --- a/crates/kryphos/src/error.rs +++ b/crates/kryphos/src/error.rs @@ -138,7 +138,7 @@ pub enum VaultError { #[snafu(display("tamper log error: {source}"))] TamperLog { /// Underlying tamper-log failure. - source: koinon::TamperLogError, + source: tekmerion::TamperLogError, }, /// A field passed to [`crate::crypto::entry_aad`] is too long to encode diff --git a/crates/kryphos/src/key.rs b/crates/kryphos/src/key.rs index 5064664..0481267 100644 --- a/crates/kryphos/src/key.rs +++ b/crates/kryphos/src/key.rs @@ -184,34 +184,34 @@ impl fmt::Display for VerifyingKey { const KEY_ID_DOMAIN: &[u8] = b"kryphos/installation/key-id/v1"; /// Computes the stable short identifier for a verifying key. -fn installation_key_id(verifying: &VerifyingKey) -> [u8; koinon::KEY_ID_LEN] { +fn installation_key_id(verifying: &VerifyingKey) -> [u8; tekmerion::KEY_ID_LEN] { let digest = blake3::keyed_hash(&blake3::hash(KEY_ID_DOMAIN).into(), verifying.as_bytes()); - let mut id = [0u8; koinon::KEY_ID_LEN]; + let mut id = [0u8; tekmerion::KEY_ID_LEN]; id.copy_from_slice( digest .as_bytes() - .get(..koinon::KEY_ID_LEN) - .unwrap_or(&[0u8; koinon::KEY_ID_LEN]), + .get(..tekmerion::KEY_ID_LEN) + .unwrap_or(&[0u8; tekmerion::KEY_ID_LEN]), ); id } -impl koinon::TipSigner for InstallationIdentity { - fn key_id(&self) -> [u8; koinon::KEY_ID_LEN] { +impl tekmerion::TipSigner for InstallationIdentity { + fn key_id(&self) -> [u8; tekmerion::KEY_ID_LEN] { installation_key_id(self.verifying_key()) } - fn sign_tip(&self, payload: &[u8]) -> [u8; koinon::TIP_SIGNATURE_LEN] { + fn sign_tip(&self, payload: &[u8]) -> [u8; tekmerion::TIP_SIGNATURE_LEN] { self.sign(payload).to_bytes() } } -impl koinon::TipVerifier for VerifyingKey { - fn key_id(&self) -> [u8; koinon::KEY_ID_LEN] { +impl tekmerion::TipVerifier for VerifyingKey { + fn key_id(&self) -> [u8; tekmerion::KEY_ID_LEN] { installation_key_id(self) } - fn verify_tip(&self, payload: &[u8], signature: &[u8; koinon::TIP_SIGNATURE_LEN]) -> bool { + fn verify_tip(&self, payload: &[u8], signature: &[u8; tekmerion::TIP_SIGNATURE_LEN]) -> bool { let signature = ed25519_dalek::Signature::from_bytes(signature); self.verify(payload, &signature).is_ok() } @@ -282,7 +282,7 @@ impl InstallationIdentity { /// Signs a tamper log entry hash, proving this installation produced it. /// - /// `entry_hash` is the 32-byte BLAKE3 hash from `koinon::tamper_log::encode_entry`. + /// `entry_hash` is the 32-byte BLAKE3 hash from `tekmerion::tamper_log::encode_entry`. #[must_use] pub fn sign_entry(&self, entry_hash: &[u8; 32]) -> ed25519_dalek::Signature { self.signing.sign(entry_hash) diff --git a/crates/kryphos/src/storage.rs b/crates/kryphos/src/storage.rs index 5869ea0..1123749 100644 --- a/crates/kryphos/src/storage.rs +++ b/crates/kryphos/src/storage.rs @@ -7,9 +7,9 @@ use std::path::{Path, PathBuf}; use compact_str::CompactString; use fs2::FileExt; use jiff::Timestamp; -use koinon::{ChainKey, LogEntryKind, TamperLog, VerificationResult}; use serde::{Deserialize, Serialize}; use snafu::ResultExt; +use tekmerion::{ChainKey, LogEntryKind, TamperLog, VerificationResult}; use zeroize::Zeroizing; use crate::crypto::{self, ENTRY_ENVELOPE_VERSION, decrypt, encrypt, entry_aad}; @@ -76,17 +76,17 @@ const AUDIT_REF_DOMAIN: &[u8] = b"kryphos/vault/audit-ref/v1"; /// question "which installation produced this log" still has an answer when the /// asking vault has no identity of its own, and it is not always "none". A log /// that carries provenance under a vault with no recorded key is reported as -/// [`koinon::TipStatus::ForeignInstallation`] — the log names someone this +/// [`tekmerion::TipStatus::ForeignInstallation`] — the log names someone this /// vault has no record of, which is worth surfacing rather than flattening into /// "unsigned". struct NoInstallation; -impl koinon::TipVerifier for NoInstallation { - fn key_id(&self) -> [u8; koinon::KEY_ID_LEN] { - [0u8; koinon::KEY_ID_LEN] +impl tekmerion::TipVerifier for NoInstallation { + fn key_id(&self) -> [u8; tekmerion::KEY_ID_LEN] { + [0u8; tekmerion::KEY_ID_LEN] } - fn verify_tip(&self, _payload: &[u8], _signature: &[u8; koinon::TIP_SIGNATURE_LEN]) -> bool { + fn verify_tip(&self, _payload: &[u8], _signature: &[u8; tekmerion::TIP_SIGNATURE_LEN]) -> bool { false } } @@ -1003,20 +1003,20 @@ impl Vault { /// /// Returns [`VaultError::TamperLog`] if the log file cannot be read. pub fn verify_tamper_log(&self) -> Result { - koinon::verify_chain(self.tamper_log_path(), &self.chain_key()).context(TamperLogSnafu) + tekmerion::verify_chain(self.tamper_log_path(), &self.chain_key()).context(TamperLogSnafu) } /// Reports which installation produced this vault's audit log. /// /// WHY the vault answers this rather than the caller reaching for - /// `koinon::verify_tip_provenance` directly: the vault is what knows + /// `tekmerion::verify_tip_provenance` directly: the vault is what knows /// whether an identity is *expected*. koinon can report that a log is /// unsigned; only the header says whether that is a vault which never had /// an identity or one whose signing has been stripped. /// /// That distinction is the migration rule (forkwright/akroasis#284). An /// unsigned log under a vault with no recorded identity is the ordinary - /// pre-provenance state and reports [`koinon::TipStatus::Unsigned`]. An + /// pre-provenance state and reports [`tekmerion::TipStatus::Unsigned`]. An /// unsigned log under a vault that *has* an identity is a downgrade with no /// innocent cause, and is refused as [`VaultError::InvalidHeader`] rather /// than reported as merely unsigned — the same asymmetry the tamper log's @@ -1027,10 +1027,10 @@ impl Vault { /// /// Returns [`VaultError::TamperLog`] if the log cannot be read, and the /// header errors of [`Self::installation_identity`]. - pub fn verify_tamper_log_provenance(&self) -> Result { + pub fn verify_tamper_log_provenance(&self) -> Result { let Some(identity) = self.installation_identity()? else { // No identity to check against; koinon reports the log's own state. - return koinon::verify_tip_provenance( + return tekmerion::verify_tip_provenance( self.tamper_log_path(), &self.chain_key(), &NoInstallation, @@ -1039,14 +1039,14 @@ impl Vault { }; let verifying = identity.verifying_key(); - let status = koinon::verify_tip_provenance( + let status = tekmerion::verify_tip_provenance( self.tamper_log_path(), &self.chain_key(), - verifying as &dyn koinon::TipVerifier, + verifying as &dyn tekmerion::TipVerifier, ) .context(TamperLogSnafu)?; - if status == koinon::TipStatus::Unsigned { + if status == tekmerion::TipStatus::Unsigned { return Err(VaultError::InvalidHeader { reason: String::from( "vault has an installation identity but its audit log tip is unsigned", diff --git a/crates/kryphos/tests/tamper_log_signing.rs b/crates/kryphos/tests/tamper_log_signing.rs index 4490c5c..24eeaaf 100644 --- a/crates/kryphos/tests/tamper_log_signing.rs +++ b/crates/kryphos/tests/tamper_log_signing.rs @@ -13,8 +13,8 @@ reason = "integration test — panics are the correct failure mode" )] -use koinon::TipStatus; use kryphos::{CredentialType, Vault}; +use tekmerion::TipStatus; const TEST_PASSPHRASE: &[u8] = b"correct horse battery staple"; diff --git a/crates/kryphos/tests/vault_tamper_audit.rs b/crates/kryphos/tests/vault_tamper_audit.rs index 6e3a5bb..7e0d322 100644 --- a/crates/kryphos/tests/vault_tamper_audit.rs +++ b/crates/kryphos/tests/vault_tamper_audit.rs @@ -7,14 +7,14 @@ reason = "integration test — panics are the correct failure mode" )] -use koinon::{ChainStatus, LogEntryKind}; use kryphos::{CredentialType, Vault, VaultError}; +use tekmerion::{ChainStatus, LogEntryKind}; const TEST_PASSPHRASE: &[u8] = b"correct horse battery staple"; /// Walks wire-format bytes and returns the byte offset of entry `target_idx`. /// -/// Mirrors `koinon::tamper_log`'s own private test helper — kryphos has no +/// Mirrors `tekmerion::tamper_log`'s own private test helper — kryphos has no /// access to it, and the wire format (`[4-byte LE len][cbor][32-byte hash]`) /// is part of the documented on-disk contract, not an implementation detail. fn entry_offset(data: &[u8], target_idx: usize) -> usize { @@ -64,7 +64,7 @@ fn vault_mutations_append_intact_tamper_log() { ]; for (idx, (name, operation)) in expected.into_iter().enumerate() { let offset = entry_offset(&data, idx); - let (entry, _hash) = koinon::tamper_log::decode_entry(&data[offset..]).unwrap(); + let (entry, _hash) = tekmerion::tamper_log::decode_entry(&data[offset..]).unwrap(); match entry.kind { LogEntryKind::VaultMutation { credential_ref, @@ -99,7 +99,7 @@ fn vault_mutations_append_intact_tamper_log() { let refs: Vec = (0..5) .map(|idx| { let offset = entry_offset(&data, idx); - let (entry, _) = koinon::tamper_log::decode_entry(&data[offset..]).unwrap(); + let (entry, _) = tekmerion::tamper_log::decode_entry(&data[offset..]).unwrap(); match entry.kind { LogEntryKind::VaultMutation { credential_ref, .. } => credential_ref.to_string(), other => panic!("entry {idx}: expected VaultMutation, got {other:?}"), diff --git a/crates/semaino/Cargo.toml b/crates/semaino/Cargo.toml index 0ed2112..40d34cf 100644 --- a/crates/semaino/Cargo.toml +++ b/crates/semaino/Cargo.toml @@ -6,7 +6,7 @@ rust-version.workspace = true license.workspace = true [dependencies] -koinon = { path = "../koinon" } +stoicheion = { path = "../stoicheion" } serde = { workspace = true } tokio = { workspace = true } snafu = { workspace = true } diff --git a/crates/semaino/src/aggregator.rs b/crates/semaino/src/aggregator.rs index 3f442d6..a5955ca 100644 --- a/crates/semaino/src/aggregator.rs +++ b/crates/semaino/src/aggregator.rs @@ -7,11 +7,11 @@ use std::collections::HashMap; -use koinon::{ +use snafu::Snafu; +use stoicheion::{ AnomalyScore, GeoSignal, TemporalBucketedBaseline, signal::{EnvironmentalDetail, GpsDetail, MeshDetail, ProximityDetail, RfDetail, SignalKind}, }; -use snafu::Snafu; use tokio::sync::mpsc; // --------------------------------------------------------------------------- @@ -224,11 +224,11 @@ impl SignalAggregator { } } -/// Extract (`day_of_week`, `hour`) from a [`koinon::Timestamp`]. +/// Extract (`day_of_week`, `hour`) from a [`stoicheion::Timestamp`]. /// /// Returns (0, 0) — Monday 00:00 — on any conversion failure so the caller /// always gets a valid bucket index. -pub(crate) fn day_hour_from_timestamp(ts: &koinon::Timestamp) -> (u8, u8) { +pub(crate) fn day_hour_from_timestamp(ts: &stoicheion::Timestamp) -> (u8, u8) { use jiff::civil::Weekday; let millis = ts.as_unix_millis(); @@ -266,7 +266,7 @@ pub(crate) fn day_hour_from_timestamp(ts: &koinon::Timestamp) -> (u8, u8) { reason = "test code: panics and unwraps acceptable in assertions" )] mod tests { - use koinon::{ + use stoicheion::{ Coordinates, Frequency, Power, Timestamp, signal::{ EnvironmentalDetail, GpsDetail, MeshDetail, NetworkDetail, OsintDetail, @@ -491,7 +491,7 @@ mod tests { #[test] fn extract_feature_returns_none_for_tracker() { - use koinon::signal::TrackerKind; + use stoicheion::signal::TrackerKind; let sig = GeoSignal::new( SignalKind::Proximity(ProximityDetail::Tracker { @@ -564,7 +564,7 @@ mod tests { fn day_hour_from_timestamp_maps_known_instants() { // NOTE: the (0, 0) conversion fallback documented on // day_hour_from_timestamp is unreachable through any public path — - // `koinon::Timestamp` only ever holds an already-valid jiff timestamp + // `stoicheion::Timestamp` only ever holds an already-valid jiff timestamp // (`now()`, the validating `from_unix_millis`, or a Deserialize impl // that delegates to jiff's own), so neither `from_millisecond` nor // `in_tz("UTC")` can fail on a value obtained from one. It is diff --git a/crates/semaino/src/alert.rs b/crates/semaino/src/alert.rs index 579dc45..c5b7750 100644 --- a/crates/semaino/src/alert.rs +++ b/crates/semaino/src/alert.rs @@ -18,7 +18,7 @@ use std::collections::HashMap; -use koinon::{ +use stoicheion::{ AnomalyScore, SignalId, Timestamp, signal::{AlertSeverity, SignalKind}, }; @@ -377,7 +377,7 @@ fn build_summary( reason = "test code: panics and unwraps acceptable in assertions" )] mod tests { - use koinon::{ + use stoicheion::{ AnomalyScore, Coordinates, Frequency, Power, Timestamp, signal::{EnvironmentalDetail, RfDetail}, }; @@ -387,11 +387,11 @@ mod tests { // ── helper builders ─────────────────────────────────────────────────────── fn rf_aggregated(score: AnomalyScore) -> AggregatedSignal { - use koinon::GeoSignal; + use stoicheion::GeoSignal; AggregatedSignal { signal: GeoSignal::new( - koinon::signal::SignalKind::Rf(RfDetail::Transmission { + stoicheion::signal::SignalKind::Rf(RfDetail::Transmission { frequency: Frequency::mhz(146), power: Power::dbm(-30.0), modulation: "FM".into(), @@ -407,11 +407,11 @@ mod tests { } fn rf_aggregated_at(score: AnomalyScore, location: Coordinates) -> AggregatedSignal { - use koinon::GeoSignal; + use stoicheion::GeoSignal; AggregatedSignal { signal: GeoSignal::new( - koinon::signal::SignalKind::Rf(RfDetail::Transmission { + stoicheion::signal::SignalKind::Rf(RfDetail::Transmission { frequency: Frequency::mhz(146), power: Power::dbm(-30.0), modulation: "FM".into(), @@ -688,11 +688,11 @@ mod tests { #[test] fn classify_environmental_elevated_alone_is_low() { - use koinon::GeoSignal; + use stoicheion::GeoSignal; let agg = AggregatedSignal { signal: GeoSignal::new( - koinon::signal::SignalKind::Environmental(EnvironmentalDetail::Temperature { + stoicheion::signal::SignalKind::Environmental(EnvironmentalDetail::Temperature { celsius: 99.0, }), Timestamp::now(), diff --git a/crates/semaino/src/convergence.rs b/crates/semaino/src/convergence.rs index 044ccc9..277b824 100644 --- a/crates/semaino/src/convergence.rs +++ b/crates/semaino/src/convergence.rs @@ -11,7 +11,7 @@ use std::collections::HashMap; -use koinon::{Coordinates, GeoSignal, Timestamp, signal::SignalKind}; +use stoicheion::{Coordinates, GeoSignal, Timestamp, signal::SignalKind}; // --------------------------------------------------------------------------- // GridCell @@ -345,7 +345,7 @@ fn cell_center(cell: GridCell, resolution: u32) -> Coordinates { reason = "test code: panics and unwraps acceptable in assertions" )] mod tests { - use koinon::{ + use stoicheion::{ Frequency, Power, Timestamp, signal::{ EnvironmentalDetail, GpsDetail, MeshDetail, NetworkDetail, OsintDetail, diff --git a/crates/semaino/src/pipeline.rs b/crates/semaino/src/pipeline.rs index fb1cd1a..51121a9 100644 --- a/crates/semaino/src/pipeline.rs +++ b/crates/semaino/src/pipeline.rs @@ -5,8 +5,8 @@ use std::time::Duration; -use koinon::GeoSignal; use serde::{Deserialize, Serialize}; +use stoicheion::GeoSignal; use tokio::sync::{broadcast, mpsc}; use tracing::Instrument as _; @@ -213,9 +213,9 @@ impl SemainoPipeline { clippy::cast_possible_truncation, reason = "Duration::as_millis() returns u128; cast to i64 is safe because i64::MAX ms is larger than any realistic eviction window" )] - let evict_before_ms = koinon::Timestamp::now().as_unix_millis() + let evict_before_ms = stoicheion::Timestamp::now().as_unix_millis() - self.time_window.as_millis() as i64; // SAFETY: Duration::as_millis() returns u128 but time_window is config-bounded; fits i64 - if let Ok(ts) = koinon::Timestamp::from_unix_millis(evict_before_ms) { + if let Ok(ts) = stoicheion::Timestamp::from_unix_millis(evict_before_ms) { self.grid.evict(ts); } @@ -290,7 +290,7 @@ impl SemainoPipeline { fn handle_aggregated(&mut self, aggregated: &AggregatedSignal) { self.grid.ingest(&aggregated.signal); - let now = koinon::Timestamp::now(); + let now = stoicheion::Timestamp::now(); // WHY(#223): this read the whole grid and then took `.first()`, under a // comment claiming it was "the cell matching the signal". A HashMap has // no first, so with more than one converged cell the alert was attributed @@ -328,7 +328,7 @@ impl SemainoPipeline { mod tests { use std::sync::{Arc, Mutex}; - use koinon::{ + use stoicheion::{ AnomalyScore, Coordinates, Frequency, GeoSignal, Power, Timestamp, signal::{GpsDetail, MeshDetail, RfDetail, SignalKind}, }; @@ -401,7 +401,7 @@ mod tests { }; assert_eq!( severities, - vec![koinon::signal::AlertSeverity::High], + vec![stoicheion::signal::AlertSeverity::High], "exactly one alert, from the outlier: an empty list means it was \ dropped crossing a saturated channel (#232), and more than one \ means the flat burst itself scored" @@ -490,7 +490,7 @@ mod tests { }; assert_eq!( severities, - vec![koinon::signal::AlertSeverity::Critical], + vec![stoicheion::signal::AlertSeverity::Critical], "one alert, Critical: three domains converged at the anomaly's own \ cell. High would mean the mesh and GPS signals were not yet in the \ grid when the anomaly was evaluated (#224)" @@ -547,7 +547,8 @@ mod tests { assert!( matches!( severity, - koinon::signal::AlertSeverity::High | koinon::signal::AlertSeverity::Critical + stoicheion::signal::AlertSeverity::High + | stoicheion::signal::AlertSeverity::Critical ), "outlier signal should produce High or Critical alert, got {severity:?}", ); @@ -606,7 +607,7 @@ mod tests { #[tokio::test] async fn pipeline_ignores_osint_no_alert() { - use koinon::signal::{OsintDetail, SignalKind}; + use stoicheion::signal::{OsintDetail, SignalKind}; let (tx, rx) = broadcast::channel::(64); let mut pipeline = SemainoPipeline::new(&SemainoConfig::default()); @@ -659,7 +660,7 @@ mod tests { // calling handle_aggregated directly against a grid that has NOT // yet seen the RF trigger signal, only a co-located Mesh signal that // arrived through the ordinary path. - use koinon::signal::MeshDetail; + use stoicheion::signal::MeshDetail; let loc = Coordinates::new(51.5, -0.1, None).expect("valid coordinates"); @@ -715,7 +716,7 @@ mod tests { ); assert_eq!( first_severity, - Some(koinon::signal::AlertSeverity::Medium), + Some(stoicheion::signal::AlertSeverity::Medium), "an Elevated score with 2-domain convergence classifies as \ Medium; a Low severity here means the RF trigger's own signal \ was missing from the grid at detection time (#224)" @@ -732,7 +733,7 @@ mod tests { /// location. #[test] fn a_convergence_in_another_cell_does_not_escalate_this_signal() { - use koinon::signal::MeshDetail; + use stoicheion::signal::MeshDetail; let elsewhere = Coordinates::new(51.5, -0.1, None).expect("valid coordinates"); let trigger_loc = Coordinates::new(48.85, 2.35, None).expect("valid coordinates"); @@ -791,7 +792,7 @@ mod tests { }; assert_eq!( first_severity, - Some(koinon::signal::AlertSeverity::Low), + Some(stoicheion::signal::AlertSeverity::Low), "the trigger's own cell holds one domain, so this must classify \ Low; Medium means an unrelated cell's convergence was used" ); diff --git a/crates/semaino/tests/smoke.rs b/crates/semaino/tests/smoke.rs index 55001d6..d6afe6d 100644 --- a/crates/semaino/tests/smoke.rs +++ b/crates/semaino/tests/smoke.rs @@ -4,9 +4,9 @@ //! that the library exposes a public test binary (required by //! TESTING/no-tests). -use koinon::signal::{RfDetail, SignalKind}; -use koinon::{GeoSignal, Power, Timestamp}; use semaino::{ConvergenceGrid, SemainoConfig, SignalAggregator}; +use stoicheion::signal::{RfDetail, SignalKind}; +use stoicheion::{GeoSignal, Power, Timestamp}; #[test] fn default_config_has_sensible_values() { diff --git a/crates/koinon/.kanon-lint-ignore b/crates/stoicheion/.kanon-lint-ignore similarity index 100% rename from crates/koinon/.kanon-lint-ignore rename to crates/stoicheion/.kanon-lint-ignore diff --git a/crates/stoicheion/Cargo.toml b/crates/stoicheion/Cargo.toml new file mode 100644 index 0000000..56910df --- /dev/null +++ b/crates/stoicheion/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "stoicheion" +description = "στοιχεῖον — the elements the Akroasis workspace is composed from" +version.workspace = true +edition.workspace = true +rust-version.workspace = true +license.workspace = true +repository.workspace = true + +[dependencies] +compact_str.workspace = true +jiff.workspace = true +serde.workspace = true +serde_json.workspace = true +snafu.workspace = true +tracing.workspace = true +ulid.workspace = true + +[dev-dependencies] +proptest = "1" + +[lints] +workspace = true diff --git a/crates/koinon/src/baseline.rs b/crates/stoicheion/src/baseline.rs similarity index 100% rename from crates/koinon/src/baseline.rs rename to crates/stoicheion/src/baseline.rs diff --git a/crates/koinon/src/baseline_tests.rs b/crates/stoicheion/src/baseline_tests.rs similarity index 100% rename from crates/koinon/src/baseline_tests.rs rename to crates/stoicheion/src/baseline_tests.rs diff --git a/crates/koinon/src/coordinates.rs b/crates/stoicheion/src/coordinates.rs similarity index 100% rename from crates/koinon/src/coordinates.rs rename to crates/stoicheion/src/coordinates.rs diff --git a/crates/koinon/src/entity.rs b/crates/stoicheion/src/entity.rs similarity index 100% rename from crates/koinon/src/entity.rs rename to crates/stoicheion/src/entity.rs diff --git a/crates/koinon/src/frequency.rs b/crates/stoicheion/src/frequency.rs similarity index 100% rename from crates/koinon/src/frequency.rs rename to crates/stoicheion/src/frequency.rs diff --git a/crates/koinon/src/hardware.rs b/crates/stoicheion/src/hardware.rs similarity index 100% rename from crates/koinon/src/hardware.rs rename to crates/stoicheion/src/hardware.rs diff --git a/crates/koinon/src/hardware_tests.rs b/crates/stoicheion/src/hardware_tests.rs similarity index 100% rename from crates/koinon/src/hardware_tests.rs rename to crates/stoicheion/src/hardware_tests.rs diff --git a/crates/koinon/src/id.rs b/crates/stoicheion/src/id.rs similarity index 100% rename from crates/koinon/src/id.rs rename to crates/stoicheion/src/id.rs diff --git a/crates/stoicheion/src/lib.rs b/crates/stoicheion/src/lib.rs new file mode 100644 index 0000000..0c4b47e --- /dev/null +++ b/crates/stoicheion/src/lib.rs @@ -0,0 +1,35 @@ +//! στοιχεῖον — the elements the Akroasis workspace is composed from. +//! +//! A stoicheion is a primary constituent, and also a letter of the alphabet: +//! the units that combine into everything written above them. This crate holds +//! that layer — what a signal, a frequency, a coordinate, a piece of hardware +//! and a moment in time *are* — and nothing that reasons about them. +//! +//! Evidence about those things (the tamper-evident log, caller authority, +//! effect receipts) lives in `tekmerion`, which depends on this crate and not +//! the reverse. + +#![deny(missing_docs)] + +pub mod baseline; +pub mod coordinates; +pub mod entity; +pub mod frequency; +pub mod hardware; +pub mod id; +pub mod power; +pub mod signal; +pub mod timestamp; + +pub use baseline::{AnomalyScore, Baseline, ScoringConfig, TemporalBucketedBaseline}; +pub use coordinates::{Coordinates, CoordinatesError, Datum}; +pub use entity::{Entity, EntityKind}; +pub use frequency::Frequency; +pub use hardware::{ + AssetRegistry, AssetStatus, ConnectionType, HardwareAsset, HardwareKind, KNOWN_USB_DEVICES, + KnownUsbDevice, MeshNodeKind, RadioKind, RegistryError, SdrKind, UsbId, lookup_usb_device, +}; +pub use id::{DeviceId, EntityId, FrequencyId, SignalId}; +pub use power::Power; +pub use signal::{Confidence, GeoSignal, SignalKind}; +pub use timestamp::{Timestamp, TimestampError}; diff --git a/crates/koinon/src/power.rs b/crates/stoicheion/src/power.rs similarity index 100% rename from crates/koinon/src/power.rs rename to crates/stoicheion/src/power.rs diff --git a/crates/koinon/src/signal.rs b/crates/stoicheion/src/signal.rs similarity index 100% rename from crates/koinon/src/signal.rs rename to crates/stoicheion/src/signal.rs diff --git a/crates/koinon/src/signal_tests.rs b/crates/stoicheion/src/signal_tests.rs similarity index 100% rename from crates/koinon/src/signal_tests.rs rename to crates/stoicheion/src/signal_tests.rs diff --git a/crates/koinon/src/timestamp.rs b/crates/stoicheion/src/timestamp.rs similarity index 100% rename from crates/koinon/src/timestamp.rs rename to crates/stoicheion/src/timestamp.rs diff --git a/crates/koinon/tests/smoke.rs b/crates/stoicheion/tests/smoke.rs similarity index 93% rename from crates/koinon/tests/smoke.rs rename to crates/stoicheion/tests/smoke.rs index 61219f3..da681b6 100644 --- a/crates/koinon/tests/smoke.rs +++ b/crates/stoicheion/tests/smoke.rs @@ -9,7 +9,7 @@ reason = "test code: panics and unwraps acceptable in assertions" )] -use koinon::{Coordinates, Frequency, Power, Timestamp}; +use stoicheion::{Coordinates, Frequency, Power, Timestamp}; #[test] fn frequency_roundtrip_mhz() { diff --git a/crates/syntonia/Cargo.toml b/crates/syntonia/Cargo.toml index 1596879..49ec9be 100644 --- a/crates/syntonia/Cargo.toml +++ b/crates/syntonia/Cargo.toml @@ -11,7 +11,7 @@ license.workspace = true hardware-serial = ["dep:serialport"] [dependencies] -koinon = { path = "../koinon" } +stoicheion = { path = "../stoicheion" } serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } toml = { workspace = true } diff --git a/crates/syntonia/src/baofeng/codec.rs b/crates/syntonia/src/baofeng/codec.rs index 183c195..850521c 100644 --- a/crates/syntonia/src/baofeng/codec.rs +++ b/crates/syntonia/src/baofeng/codec.rs @@ -1,7 +1,7 @@ //! Channel encode/decode between raw EEPROM bytes and the Channel model. -use koinon::Frequency; use snafu::Snafu; +use stoicheion::Frequency; use crate::baofeng::bcd::{self, BcdError}; use crate::baofeng::image::MemoryImage; diff --git a/crates/syntonia/src/channel.rs b/crates/syntonia/src/channel.rs index b7a25e9..616e1a1 100644 --- a/crates/syntonia/src/channel.rs +++ b/crates/syntonia/src/channel.rs @@ -1,7 +1,7 @@ //! Radio channel data model. -use koinon::Frequency; use serde::{Deserialize, Serialize}; +use stoicheion::Frequency; use crate::tone::ToneMode; use crate::types::{Bandwidth, FrequencyOffset, PowerLevel, ScanMode}; diff --git a/crates/syntonia/src/export/csv.rs b/crates/syntonia/src/export/csv.rs index 8192e31..ec96061 100644 --- a/crates/syntonia/src/export/csv.rs +++ b/crates/syntonia/src/export/csv.rs @@ -171,7 +171,7 @@ fn channel_to_record(ch: &Channel) -> [String; 19] { reason = "test code: panics and unwraps acceptable in assertions" )] mod tests { - use koinon::Frequency; + use stoicheion::Frequency; use super::*; use crate::import::csv::import_chirp_csv_reader; diff --git a/crates/syntonia/src/hardware/detect.rs b/crates/syntonia/src/hardware/detect.rs index e204524..800b0ba 100644 --- a/crates/syntonia/src/hardware/detect.rs +++ b/crates/syntonia/src/hardware/detect.rs @@ -3,8 +3,8 @@ use std::io::{self, Read, Write}; use std::time::Duration; -use koinon::RadioKind; use snafu::{ResultExt, Snafu}; +use stoicheion::RadioKind; use crate::baofeng::variant::{ BF_F8HP_PREFIXES, MAGIC_UV5R_PROBE, UV5R_PREFIXES, UV5RM_PLUS_PREFIXES, diff --git a/crates/syntonia/src/hardware/warnings.rs b/crates/syntonia/src/hardware/warnings.rs index fd0c9ea..800bf8c 100644 --- a/crates/syntonia/src/hardware/warnings.rs +++ b/crates/syntonia/src/hardware/warnings.rs @@ -204,7 +204,7 @@ mod tests { #[test] fn multiple_radios_generates_warning() { - use koinon::RadioKind; + use stoicheion::RadioKind; use crate::hardware::detect::{RadioIdent, VariantConfig}; @@ -244,7 +244,7 @@ mod tests { #[test] fn single_radio_generates_no_detection_warning() { - use koinon::RadioKind; + use stoicheion::RadioKind; use crate::hardware::detect::{RadioIdent, VariantConfig}; diff --git a/crates/syntonia/src/import/csv.rs b/crates/syntonia/src/import/csv.rs index 601ba39..937b52f 100644 --- a/crates/syntonia/src/import/csv.rs +++ b/crates/syntonia/src/import/csv.rs @@ -3,8 +3,8 @@ use std::io::Read; use std::path::Path; -use koinon::Frequency; use snafu::{ResultExt, Snafu}; +use stoicheion::Frequency; use crate::channel::Channel; use crate::plan::FrequencyPlan; diff --git a/crates/syntonia/src/import/img.rs b/crates/syntonia/src/import/img.rs index b70ae06..57d16d1 100644 --- a/crates/syntonia/src/import/img.rs +++ b/crates/syntonia/src/import/img.rs @@ -109,7 +109,7 @@ pub fn import_img_bytes(data: &[u8]) -> Result { reason = "test code: panics and unwraps acceptable in assertions" )] mod tests { - use koinon::Frequency; + use stoicheion::Frequency; use super::*; use crate::baofeng::codec; diff --git a/crates/syntonia/src/plan.rs b/crates/syntonia/src/plan.rs index 4d37758..bd115b6 100644 --- a/crates/syntonia/src/plan.rs +++ b/crates/syntonia/src/plan.rs @@ -80,7 +80,7 @@ impl FrequencyPlan { reason = "test code: panics and unwraps acceptable in assertions" )] mod tests { - use koinon::Frequency; + use stoicheion::Frequency; use super::*; use crate::tone::ToneMode; diff --git a/crates/syntonia/src/types.rs b/crates/syntonia/src/types.rs index f052aa4..d26315c 100644 --- a/crates/syntonia/src/types.rs +++ b/crates/syntonia/src/types.rs @@ -1,7 +1,7 @@ //! Supporting types for channel configuration. -use koinon::Frequency; use serde::{Deserialize, Serialize}; +use stoicheion::Frequency; /// Frequency offset configuration for repeater operation. #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] diff --git a/crates/syntonia/src/validate.rs b/crates/syntonia/src/validate.rs index ba6fde4..67d7b67 100644 --- a/crates/syntonia/src/validate.rs +++ b/crates/syntonia/src/validate.rs @@ -2,7 +2,7 @@ use std::collections::HashSet; -use koinon::Frequency; +use stoicheion::Frequency; use crate::channel::Channel; use crate::plan::FrequencyPlan; diff --git a/crates/syntonia/src/yaesu/codec.rs b/crates/syntonia/src/yaesu/codec.rs index 83dcbc9..87ff2c1 100644 --- a/crates/syntonia/src/yaesu/codec.rs +++ b/crates/syntonia/src/yaesu/codec.rs @@ -107,7 +107,7 @@ mod tests { let channel = Channel { index: 0, name: String::new(), - rx_freq: koinon::Frequency::mhz(146), + rx_freq: stoicheion::Frequency::mhz(146), tx_freq: None, offset: crate::types::FrequencyOffset::None, tone: crate::tone::ToneMode::None, diff --git a/crates/syntonia/src/yaesu/variant.rs b/crates/syntonia/src/yaesu/variant.rs index 6349d26..2e3c2a9 100644 --- a/crates/syntonia/src/yaesu/variant.rs +++ b/crates/syntonia/src/yaesu/variant.rs @@ -4,7 +4,7 @@ //! — see this module's parent (`super`) for the derivation's licence and //! upstream. The clone-mode protocol itself is not yet documented. -use koinon::Frequency; +use stoicheion::Frequency; use crate::validate::RadioConstraints; diff --git a/crates/koinon/Cargo.toml b/crates/tekmerion/Cargo.toml similarity index 75% rename from crates/koinon/Cargo.toml rename to crates/tekmerion/Cargo.toml index aad6e6a..e86bf31 100644 --- a/crates/koinon/Cargo.toml +++ b/crates/tekmerion/Cargo.toml @@ -1,6 +1,6 @@ [package] -name = "koinon" -description = "κοινόν — common/shared types for the Akroasis workspace" +name = "tekmerion" +description = "τεκμήριον — conclusive evidence: tamper-evident log, caller authority, effect receipts" version.workspace = true edition.workspace = true rust-version.workspace = true @@ -8,23 +8,23 @@ license.workspace = true repository.workspace = true [dependencies] -ulid.workspace = true +blake3.workspace = true +ciborium.workspace = true +compact_str.workspace = true +fs2.workspace = true jiff.workspace = true serde.workspace = true serde_json.workspace = true snafu.workspace = true +stoicheion = { path = "../stoicheion" } +subtle.workspace = true tracing.workspace = true -compact_str.workspace = true -blake3.workspace = true -ciborium.workspace = true zeroize.workspace = true -subtle.workspace = true -fs2.workspace = true [dev-dependencies] tempfile.workspace = true -proptest = "1" toml.workspace = true +ulid.workspace = true [lints] workspace = true diff --git a/crates/koinon/src/caller.rs b/crates/tekmerion/src/caller.rs similarity index 99% rename from crates/koinon/src/caller.rs rename to crates/tekmerion/src/caller.rs index 4ee923f..3ac8a2a 100644 --- a/crates/koinon/src/caller.rs +++ b/crates/tekmerion/src/caller.rs @@ -10,7 +10,6 @@ use std::fmt; use serde::{Deserialize, Serialize}; use snafu::{OptionExt, ensure}; -use crate::Timestamp; pub use crate::caller_error::CallerContractError; use crate::caller_error::{ AuthorityUnavailableSnafu, ExpiredSnafu, IdentityReferenceMismatchSnafu, @@ -23,6 +22,7 @@ pub use crate::caller_ref::{ CallerRef, CapabilityRef, EffectRef, EvidenceDigest, PersonaRef, PolicyEpoch, ReceiptDigest, SchemaEpoch, ScopeRef, }; +use stoicheion::Timestamp; /// Caller-context schema version understood by this crate. pub const CALLER_CONTEXT_VERSION: u16 = 1; diff --git a/crates/koinon/src/caller_error.rs b/crates/tekmerion/src/caller_error.rs similarity index 100% rename from crates/koinon/src/caller_error.rs rename to crates/tekmerion/src/caller_error.rs diff --git a/crates/koinon/src/caller_ref.rs b/crates/tekmerion/src/caller_ref.rs similarity index 100% rename from crates/koinon/src/caller_ref.rs rename to crates/tekmerion/src/caller_ref.rs diff --git a/crates/koinon/src/effect_receipt.rs b/crates/tekmerion/src/effect_receipt.rs similarity index 99% rename from crates/koinon/src/effect_receipt.rs rename to crates/tekmerion/src/effect_receipt.rs index b793ab9..22cd5e3 100644 --- a/crates/koinon/src/effect_receipt.rs +++ b/crates/tekmerion/src/effect_receipt.rs @@ -7,12 +7,12 @@ use serde::{Deserialize, Deserializer, Serialize}; use snafu::Snafu; -use crate::Timestamp; use crate::caller::{ AuthorizedCaller, CallerRef, CapabilityRef, EffectRef, EvidenceDigest, PolicyEpoch, ReceiptDigest, SchemaEpoch, }; use crate::effect_receipt_state::{PendingIntent, RecoveryAuthorization}; +use stoicheion::Timestamp; /// Current serialized effect-receipt schema version. pub const EFFECT_RECEIPT_VERSION: u16 = 1; diff --git a/crates/koinon/src/effect_receipt_state.rs b/crates/tekmerion/src/effect_receipt_state.rs similarity index 99% rename from crates/koinon/src/effect_receipt_state.rs rename to crates/tekmerion/src/effect_receipt_state.rs index 2f5163e..5ac0d85 100644 --- a/crates/koinon/src/effect_receipt_state.rs +++ b/crates/tekmerion/src/effect_receipt_state.rs @@ -2,12 +2,12 @@ use snafu::Snafu; -use crate::Timestamp; use crate::caller::{EffectRef, EvidenceDigest, ReceiptDigest, SchemaEpoch}; use crate::effect_receipt::{ EffectDescriptor, EffectOutcome, EffectReceipt, EffectReceiptError, ReceiptEvent, RecoveryRelation, }; +use stoicheion::Timestamp; /// Append-only application ledger for minimized effect receipts. /// diff --git a/crates/koinon/src/lib.rs b/crates/tekmerion/src/lib.rs similarity index 60% rename from crates/koinon/src/lib.rs rename to crates/tekmerion/src/lib.rs index 582ee05..987d28f 100644 --- a/crates/koinon/src/lib.rs +++ b/crates/tekmerion/src/lib.rs @@ -1,24 +1,29 @@ -//! κοινόν — shared foundational types for the Akroasis workspace. +//! τεκμήριον — conclusive evidence about what the system observed and did. +//! +//! Aristotle separates a *semeion*, a sign that suggests, from a *tekmerion*, +//! a proof that settles. This crate holds the second kind: the tamper-evident +//! log and its keyed hash chain, the authority a caller actually carries, and +//! the receipts an effect leaves behind. Each of them answers "can this be +//! shown?" rather than "what is this?". +//! +//! The distinction is load-bearing rather than decorative. `semaino` produces +//! signals — indications, which may be wrong. Everything here produces records +//! that can be checked against a key, and refuses to report a claim it cannot +//! substantiate. +//! +//! Built on `stoicheion`, which supplies the vocabulary these records are +//! written in. The dependency runs one way: evidence knows what a timestamp +//! and an entity are; the elements know nothing of evidence. #![deny(missing_docs)] -pub mod baseline; pub mod caller; mod caller_error; mod caller_ref; -pub mod coordinates; pub mod effect_receipt; mod effect_receipt_state; -pub mod entity; -pub mod frequency; -pub mod hardware; -pub mod id; -pub mod power; -pub mod signal; pub mod tamper_log; -pub mod timestamp; -pub use baseline::{AnomalyScore, Baseline, ScoringConfig, TemporalBucketedBaseline}; pub use caller::{ AuthorityClaims, AuthorityClaimsBuilder, AuthorityDecision, AuthorityGrant, AuthorizationDenial, AuthorizationRequirement, AuthorizedCaller, CALLER_CONTEXT_VERSION, @@ -27,7 +32,6 @@ pub use caller::{ PrincipalSource, ReceiptDigest, RevocationState, SchemaEpoch, ScopeRef, TrustState, ValidatedCaller, authorize_caller, }; -pub use coordinates::{Coordinates, CoordinatesError, Datum}; pub use effect_receipt::{ EFFECT_RECEIPT_VERSION, EffectDescriptor, EffectOutcome, EffectReceipt, EffectReceiptError, ReceiptContext, ReceiptEvent, RecoveryRelation, ValidatedReceiptLink, @@ -36,19 +40,9 @@ pub use effect_receipt_state::{ EffectReceiptSink, OutcomeAppendError, PendingIntent, PendingOutcome, PersistedIntent, PersistedOutcome, ReceiptTransitionError, RecoveryAuthorization, RecoveryTicket, }; -pub use entity::{Entity, EntityKind}; -pub use frequency::Frequency; -pub use hardware::{ - AssetRegistry, AssetStatus, ConnectionType, HardwareAsset, HardwareKind, KNOWN_USB_DEVICES, - KnownUsbDevice, MeshNodeKind, RadioKind, RegistryError, SdrKind, UsbId, lookup_usb_device, -}; -pub use id::{DeviceId, EntityId, FrequencyId, SignalId}; -pub use power::Power; -pub use signal::{Confidence, GeoSignal, SignalKind}; pub use tamper_log::{ CHAIN_KEY_LEN, ChainKey, ChainStatus, DEFAULT_MAX_FILE_BYTES, KEY_ID_LEN, LogEntry, LogEntryKind, MAX_ENTRY_BYTES, SegmentChainStatus, TIP_SIGNATURE_LEN, TamperLog, TamperLogConfig, TamperLogError, TipProvenance, TipSigner, TipStatus, TipVerifier, VerificationResult, verify_chain, verify_segment_chain, verify_tip_provenance, }; -pub use timestamp::{Timestamp, TimestampError}; diff --git a/crates/koinon/src/tamper_log.rs b/crates/tekmerion/src/tamper_log.rs similarity index 100% rename from crates/koinon/src/tamper_log.rs rename to crates/tekmerion/src/tamper_log.rs diff --git a/crates/koinon/src/tamper_log_codec_tests.rs b/crates/tekmerion/src/tamper_log_codec_tests.rs similarity index 100% rename from crates/koinon/src/tamper_log_codec_tests.rs rename to crates/tekmerion/src/tamper_log_codec_tests.rs diff --git a/crates/koinon/src/tamper_log_entry.rs b/crates/tekmerion/src/tamper_log_entry.rs similarity index 99% rename from crates/koinon/src/tamper_log_entry.rs rename to crates/tekmerion/src/tamper_log_entry.rs index 1b93f23..8f85a0f 100644 --- a/crates/koinon/src/tamper_log_entry.rs +++ b/crates/tekmerion/src/tamper_log_entry.rs @@ -3,7 +3,7 @@ use compact_str::CompactString; use serde::{Deserialize, Serialize}; -use crate::{EntityId, SignalId}; +use stoicheion::{EntityId, SignalId}; /// The kind of event recorded in a [`crate::tamper_log::LogEntry`]. #[non_exhaustive] diff --git a/crates/koinon/src/tamper_log_lock.rs b/crates/tekmerion/src/tamper_log_lock.rs similarity index 100% rename from crates/koinon/src/tamper_log_lock.rs rename to crates/tekmerion/src/tamper_log_lock.rs diff --git a/crates/koinon/src/tamper_log_recovery_tests.rs b/crates/tekmerion/src/tamper_log_recovery_tests.rs similarity index 100% rename from crates/koinon/src/tamper_log_recovery_tests.rs rename to crates/tekmerion/src/tamper_log_recovery_tests.rs diff --git a/crates/koinon/src/tamper_log_rotation.rs b/crates/tekmerion/src/tamper_log_rotation.rs similarity index 100% rename from crates/koinon/src/tamper_log_rotation.rs rename to crates/tekmerion/src/tamper_log_rotation.rs diff --git a/crates/koinon/src/tamper_log_seal.rs b/crates/tekmerion/src/tamper_log_seal.rs similarity index 98% rename from crates/koinon/src/tamper_log_seal.rs rename to crates/tekmerion/src/tamper_log_seal.rs index 1d88882..9446f7f 100644 --- a/crates/koinon/src/tamper_log_seal.rs +++ b/crates/tekmerion/src/tamper_log_seal.rs @@ -41,6 +41,17 @@ use super::{IoSnafu, TamperLogError}; pub const CHAIN_KEY_LEN: usize = 32; /// Domain-separation tag for the keyed genesis root. +// WARNING: the four domain separators below still read `koinon/...` after this +// crate was renamed to `tekmerion`, and they must. They are not names — they +// are keyed-hash inputs. `GENESIS_DOMAIN` seeds every chain's root hash and the +// seal domains key every MAC, so changing one byte re-roots the chain and +// invalidates every tamper log already on disk: existing logs would verify as +// broken, which is indistinguishable from the tampering the log exists to +// detect. +// +// A rename that "finishes the job" here is a silent, unrecoverable break. If a +// domain ever must change, it changes as a versioned migration with a reader +// that accepts both, never as a search-and-replace. const GENESIS_DOMAIN: &[u8] = b"koinon/tamper-log/genesis/v1"; /// Domain-separation tag for the seal MAC. diff --git a/crates/koinon/src/tamper_log_segments.rs b/crates/tekmerion/src/tamper_log_segments.rs similarity index 100% rename from crates/koinon/src/tamper_log_segments.rs rename to crates/tekmerion/src/tamper_log_segments.rs diff --git a/crates/koinon/src/tamper_log_tests.rs b/crates/tekmerion/src/tamper_log_tests.rs similarity index 99% rename from crates/koinon/src/tamper_log_tests.rs rename to crates/tekmerion/src/tamper_log_tests.rs index d3357bb..a887752 100644 --- a/crates/koinon/src/tamper_log_tests.rs +++ b/crates/tekmerion/src/tamper_log_tests.rs @@ -5,7 +5,7 @@ use compact_str::CompactString; use ulid::Ulid; use super::*; -use crate::{EntityId, SignalId}; +use stoicheion::{EntityId, SignalId}; fn test_key() -> ChainKey { ChainKey::from_bytes([0x5A; CHAIN_KEY_LEN]) diff --git a/crates/koinon/src/tamper_log_verify.rs b/crates/tekmerion/src/tamper_log_verify.rs similarity index 100% rename from crates/koinon/src/tamper_log_verify.rs rename to crates/tekmerion/src/tamper_log_verify.rs