Skip to content
Merged
Show file tree
Hide file tree
Changes from 57 commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
cef4ce7
chore(deps): update tinymemory subproject commit
senamakel Aug 11, 2026
2447a68
chore(deps): update tinymemory vendor dependency
senamakel Aug 11, 2026
d28e530
chore(deps): update tinymemory vendor dependency
senamakel Aug 11, 2026
02d2706
chore(deps): update tinymemory subproject commit
senamakel Aug 11, 2026
9d88bfa
fix(driver): handle missing configuration gracefully
senamakel Aug 11, 2026
4e61f57
fix(driver): handle missing configuration gracefully
senamakel Aug 11, 2026
c65e1ce
fix(guard): correct policy evaluation for empty memory blocks
senamakel Aug 11, 2026
80559a3
fix(guard): remove unused import in policy.rs
senamakel Aug 11, 2026
1ecb0c0
fix(memory): handle empty memory list in retrieval
senamakel Aug 11, 2026
2036196
fix(registry): handle duplicate module registrations gracefully
senamakel Aug 11, 2026
9fab741
feat(modules): add memory module
senamakel Aug 11, 2026
3554064
fix(binding): handle missing memory binding gracefully
senamakel Aug 11, 2026
ed8f1a5
fix(memory-tests): correct test assertion for memory allocation
senamakel Aug 11, 2026
2b86002
fix(memory_tests): correct test assertion for memory retrieval
senamakel Aug 11, 2026
012fe1b
fix(registry): handle duplicate module registrations gracefully
senamakel Aug 11, 2026
f877b12
fix: reformat long lines in memory module and tests
senamakel Aug 11, 2026
2e755f7
chore(deps): update tinymemory subproject commit
senamakel Aug 11, 2026
573ca46
fix(binding): add missing Module variant to driver class conversion
senamakel Aug 11, 2026
00eb8d4
feat(memory): add boot-time policy for module loading
senamakel Aug 11, 2026
63d394f
refactor(memory): wire module-backed driver into memory binding
senamakel Aug 11, 2026
014db51
fix(memory): replace module driver with null provider due to trait mi…
senamakel Aug 11, 2026
a2bd24c
fix(memory): handle missing vendor module in memory driver
senamakel Aug 11, 2026
fa7d7b3
feat(memory): wire ModuleMemoryProvider into the binding for the Modu…
senamakel Aug 11, 2026
c4cde73
fix: restore `module_adapter` module declaration before doc comment
senamakel Aug 11, 2026
2ea4d56
fix(module_adapter): correct test assertion for memory retrieval
senamakel Aug 11, 2026
b0d16ab
fix(test): use typed constant for memory category in round-trip test
senamakel Aug 11, 2026
ddb5827
fix: reorder module declarations to match dependency order
senamakel Aug 11, 2026
c2bb07c
fix(memory): replace module adapter with null provider
senamakel Aug 11, 2026
e3f62a3
feat(memory): wire Module driver class to the real module provider
senamakel Aug 11, 2026
72eabd8
chore(deps): update tinymemory subproject commit
senamakel Aug 12, 2026
09f417a
feat(registry): update tinymemory module to v0.3.0 with platform assets
senamakel Aug 12, 2026
be3e364
fix(registry): remove tinymemory from pending-release list
senamakel Aug 12, 2026
4716050
refactor(registry): replace placeholder docs for tinymemory with rele…
senamakel Aug 12, 2026
dea8a9d
feat(registry): add doc comment for the ALL constant
senamakel Aug 12, 2026
fb5f188
test(memory): update asset test to verify per-host completeness
senamakel Aug 12, 2026
a16ebd2
chore: files changed Cargo.lock,vendor/tinymemory
senamakel Aug 12, 2026
d8b06c0
chore: files changed app/src-tauri/Cargo.lock
senamakel Aug 12, 2026
5b7b0ae
fix(binding): handle missing memory binding gracefully
senamakel Aug 12, 2026
21f4fde
fix(memory): handle missing binding key in binding lookup
senamakel Aug 12, 2026
7281747
fix(memory): handle missing binding key in binding lookup
senamakel Aug 12, 2026
21a916e
fix(runtime): restore context field for thread safety
senamakel Aug 12, 2026
1981c4b
fix(memory): handle empty memory list in retrieval
senamakel Aug 12, 2026
7bf817f
fix(modules): pass explicit operation label to proxy calls
senamakel Aug 12, 2026
fd7652e
fix(memory): pass operation name to proxy method
senamakel Aug 12, 2026
823573d
fix(boot): handle missing boot module gracefully
senamakel Aug 12, 2026
195398d
fix(boot): handle missing boot module gracefully
senamakel Aug 12, 2026
7fa2365
fix(boot): handle missing boot module gracefully
senamakel Aug 12, 2026
bb2c4cc
chore(deps): update Cargo.lock for new dependencies and version bumps
senamakel Aug 12, 2026
02e4e80
fix(boot): correct ModuleRecord path in should_eager_load
senamakel Aug 12, 2026
a2d6d53
chore: files changed app/src-tauri/Cargo.lock
senamakel Aug 12, 2026
f64dda5
fix(module_adapter): correct test assertion for memory retrieval
senamakel Aug 12, 2026
a610f5e
fix(module_adapter): apply thread safety fix from AR-004 to prevent d…
senamakel Aug 12, 2026
735e5ca
fix: correct test assertion for memory guard policy
senamakel Aug 12, 2026
3285ae7
test(module-adapter): reformat assertion for readability
senamakel Aug 12, 2026
17d2a72
chore(deps): update Cargo.lock for new dependencies and version bumps
senamakel Aug 12, 2026
e901c9b
fix(binding): return reported class alongside provider in build
senamakel Aug 12, 2026
bda2e3e
test(binding): add tests for module driver class reporting
senamakel Aug 12, 2026
ef1de98
chore(deps): update Cargo.lock to reflect dependency changes
senamakel Aug 12, 2026
8367cb5
chore(deps): update Cargo.lock with new dependencies and version bumps
senamakel Aug 12, 2026
90ff667
chore(deps): update Cargo.lock to reflect dependency changes
senamakel Aug 12, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions src/core/runtime/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,13 @@ pub async fn init_stores(
// vectors into the wrong embedding space or make a sync run look empty
// instead of broken.
crate::openhuman::memory::host_impls::install_memory_host_seams(Arc::new(cfg.clone()));
// Publish the config a module-backed memory driver should load
// against, before the binding below can construct one. Boot-only and
// idempotent (first call wins) — see `modules::memory::set_modules_policy`
// for why this must be a process-global rather than threaded through
// `MemoryBinding::for_workspace`.
#[cfg(feature = "modules")]
crate::openhuman::modules::memory::set_modules_policy(Arc::new(cfg.clone()));
match crate::openhuman::memory::global::init(cfg.workspace_dir.clone()) {
Ok(_) => log::info!(
"[boot] memory::global initialized (workspace={})",
Expand Down
26 changes: 25 additions & 1 deletion src/core/subsystem/driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,16 +46,39 @@ pub enum DriverClass {
/// An out-of-process backend reached through a transport adapter over a
/// documented wire contract.
External,
/// A loadable native module: a `cdylib` admitted through tinybus's ABI,
/// manifest and digest gates and reached over the in-process module bus.
///
/// Neither of the two classes above fits, and the difference is the policy
/// this class gates:
///
/// - not [`Self::Embedded`], because the code is **not compiled into this
/// binary**. It is downloaded, verified against a digest pinned in
/// `modules::registry`, and `dlopen`ed. Whether it is present at all is a
/// runtime fact, so a capability set derived from it can be empty on a
/// platform no artifact is published for.
/// - not [`Self::External`], because there is **no egress and no process
/// boundary**. It shares this address space, these privileges and this
/// crash domain, so the trust checks that make sense for a remote backend
/// (endpoint allowlisting, TLS, credential scoping) are neither applicable
/// nor sufficient. What protects the host is admission, not isolation.
///
/// Treating a module as `External` would apply egress policy to something
/// that makes no network calls while implying an isolation the loader does
/// not provide; treating it as `Embedded` would claim a compile-time
/// guarantee that a downloaded artifact does not have.
Module,
/// A stub advertising zero capabilities — what a compiled-out or
/// unconfigured subsystem binds to.
Null,
}

impl DriverClass {
/// Every class, in declaration order.
pub const ALL: [DriverClass; 3] = [
pub const ALL: [DriverClass; 4] = [
DriverClass::Embedded,
DriverClass::External,
DriverClass::Module,
DriverClass::Null,
];

Expand All @@ -69,6 +92,7 @@ impl DriverClass {
match self {
Self::Embedded => "embedded",
Self::External => "external",
Self::Module => "module",
Self::Null => "null",
}
}
Expand Down
64 changes: 57 additions & 7 deletions src/openhuman/memory/binding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -254,14 +254,15 @@ pub(crate) fn reserved_class(id: &str) -> Option<DriverClass> {

/// The contract's driver class in the kernel's generic vocabulary.
///
/// A total three-arm match, which is why both enums were shaped one-for-one.
/// A total match, which is why both enums were shaped one-for-one.
/// The kernel's own enum is deliberately not replaced by the contract's: it is
/// shared with the subsystems that come after memory, which must not inherit
/// their vocabulary from a *memory* crate.
fn from_contract_class(class: ContractDriverClass) -> DriverClass {
match class {
ContractDriverClass::Embedded => DriverClass::Embedded,
ContractDriverClass::External => DriverClass::External,
ContractDriverClass::Module => DriverClass::Module,
ContractDriverClass::Null => DriverClass::Null,
}
}
Expand Down Expand Up @@ -307,18 +308,67 @@ pub fn admit(cfg: &MemorySubsystemConfig) -> Result<(String, DriverClass), Fallb
fn build(workspace_dir: &Path, cfg: &MemorySubsystemConfig) -> MemoryBinding {
match admit(cfg) {
Ok((driver_id, class)) => {
let provider: Arc<dyn MemoryProvider> = match class {
// Both the provider *and* the class it should be reported under. The
// two can differ: a build without the `modules` feature admits the
// module class but can only bind a placeholder, and reporting the
// admitted class there would advertise a module-backed driver with a
// null behind it — a live surface with no store, which is exactly
// what status output exists to make visible.
let (provider, class): (Arc<dyn MemoryProvider>, DriverClass) = match class {
// Construction is deliberately sync and I/O-free: this runs on
// `CoreContext::memory_binding`, which ~4000 pre-boot tests
// call with no tokio runtime. The driver resolves its client on
// first use — see `driver::embedded`'s module docs.
DriverClass::Embedded => {
Arc::new(EmbeddedMemoryProvider::new(workspace_dir, cfg.hooks))
}
DriverClass::Null => Arc::new(NullMemoryProvider::new()),
DriverClass::Embedded => (
Arc::new(EmbeddedMemoryProvider::new(workspace_dir, cfg.hooks)),
DriverClass::Embedded,
),
DriverClass::Null => (Arc::new(NullMemoryProvider::new()), DriverClass::Null),
// Unreachable: `admit` refuses every external driver above, so
// this arm cannot bind a transport that does not exist yet.
DriverClass::External => Arc::new(NullMemoryProvider::new()),
// Reported as `Null`, for the same reason as the arm below: what
// bound is a placeholder, and status must say so.
DriverClass::External => (Arc::new(NullMemoryProvider::new()), DriverClass::Null),
// This function builds an `Arc<dyn tinycortex_api::provider::
// MemoryProvider>`, while `modules::memory::ModuleMemoryProvider`
// implements `tinymemory_api::provider::MemoryProvider`. Those are
// two different traits from two different crates.
// `TinyMemoryContractAdapter` is the bridge: it wraps the module
// provider and implements the tinycortex-side trait by converting
// each call across the seam (see its module docs for why most
// conversions destructure exhaustively while a few cross by serde
// round trip). It is a temporary bridge — it exists to be deleted
// once the binding itself migrates onto the tinymemory contract,
// at which point `ModuleMemoryProvider` binds directly here.
//
// Gated on the `modules` feature because the concrete provider
// type lives behind it (unlike the adapter above, which is
// generic and feature-independent). A build without `modules`
// cannot load the module driver at all, so it falls back to the
// same placeholder every other inadmissible driver gets, logged
// loudly rather than silently — kernel.md §3.7.
#[cfg(feature = "modules")]
DriverClass::Module => (
Arc::new(
crate::openhuman::memory::driver::module_adapter::TinyMemoryContractAdapter::new(
Arc::new(
crate::openhuman::modules::memory::ModuleMemoryProvider::from_boot_policy(),
),
),
),
DriverClass::Module,
),
#[cfg(not(feature = "modules"))]
DriverClass::Module => {
log::warn!(
"[memory:binding] workspace={} configured driver='{}' resolves to the \
module class, but this build was compiled without the `modules` \
feature; falling back to the null placeholder",
workspace_dir.display(),
driver_id,
);
(Arc::new(NullMemoryProvider::new()), DriverClass::Null)
}
};
// The configured trust state for the driver that actually bound.
// Absent `[subsystems.memory.drivers.<id>]` entry ⇒ the fail-closed
Expand Down
34 changes: 34 additions & 0 deletions src/openhuman/memory/binding_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -636,3 +636,37 @@ fn the_embedded_driver_never_disables_memory() {
let binding = for_workspace(dir.path(), &MemorySubsystemConfig::default()).expect("binds");
assert!(!binding.disables_memory());
}

// A build that admits the module class but cannot construct a module-backed
// provider must not *report* the module class. `bind_provider` receives the
// class that status, `modules.status` and the boot log all read, so passing the
// admitted class while binding a placeholder advertises a live module-backed
// surface with a null store behind it — the one failure this codebase's drift
// guards exist to prevent, and the shape a reviewer caught here.

#[cfg(not(feature = "modules"))]
#[test]
fn a_module_driver_reports_the_null_class_when_the_feature_is_off() {
let cfg = cfg_with_class("tinymemory", "module");
let binding = super::build(std::path::Path::new("/tmp/openhuman-binding-test"), &cfg);
assert_eq!(
binding.class(),
crate::core::subsystem::DriverClass::Null,
"a placeholder must not be reported as module-backed"
);
}

#[cfg(feature = "modules")]
#[test]
fn a_module_driver_reports_the_module_class_when_the_feature_is_on() {
// The other direction, so the arm above cannot be "fixed" by making every
// module binding report Null. Construction stays I/O-free, so this needs no
// runtime and loads nothing.
let cfg = cfg_with_class("tinymemory", "module");
let binding = super::build(std::path::Path::new("/tmp/openhuman-binding-test"), &cfg);
assert_eq!(
binding.class(),
crate::core::subsystem::DriverClass::Module,
"a real module binding must report the module class"
);
}
1 change: 1 addition & 0 deletions src/openhuman/memory/driver/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@
//! would be meaningless.

pub mod embedded;
pub mod module_adapter;
Loading
Loading