init: Engram v0.1 — native memory substrate for accumulating intelligence

Memory is not stored and retrieved — it is activated and propagated.
Implements the spreading activation model with salience decay, typed edges,
four memory tiers, and flat cosine vector search over a sled embedded store.
This commit is contained in:
Will Anderson
2026-04-27 15:37:42 -05:00
commit 1a609502c8
20 changed files with 2375 additions and 0 deletions
+13
View File
@@ -0,0 +1,13 @@
[package]
name = "engram-ffi"
version = "0.1.0"
edition = "2021"
description = "C FFI bindings for engram-core"
license = "MIT"
[lib]
crate-type = ["cdylib", "staticlib"]
[dependencies]
engram-core = { path = "../engram-core" }
uuid = { version = "1", features = ["v4", "serde"] }
+118
View File
@@ -0,0 +1,118 @@
/// C FFI stubs for engram-core.
///
/// These are minimal stubs for v0.1 — enough to link from Kotlin, TypeScript (via WASM
/// or Node native addon), and Go. Full binding generation will use cbindgen in v0.2.
///
/// All pointers passed across the FFI boundary must remain valid for the duration of
/// the call. Strings are null-terminated UTF-8. The caller owns all returned heap memory
/// and must free it via the corresponding `engram_free_*` function.
///
/// # Safety
/// All functions in this module are `unsafe` because they accept raw pointers.
/// Callers are responsible for ensuring pointer validity and correct lifetimes.
use engram_core::EngramDb;
use std::ffi::{CStr, CString};
use std::os::raw::c_char;
use std::path::Path;
/// Opaque handle to an open EngramDb instance.
pub struct EngramHandle {
db: EngramDb,
}
/// Open an engram database at the given path.
///
/// Returns a heap-allocated handle on success, or null on failure.
/// The caller must eventually call `engram_close` to free the handle.
///
/// # Safety
/// `path` must be a valid, null-terminated UTF-8 string.
#[no_mangle]
pub unsafe extern "C" fn engram_open(path: *const c_char) -> *mut EngramHandle {
if path.is_null() {
return std::ptr::null_mut();
}
let path_str = match CStr::from_ptr(path).to_str() {
Ok(s) => s,
Err(_) => return std::ptr::null_mut(),
};
match EngramDb::open(Path::new(path_str)) {
Ok(db) => Box::into_raw(Box::new(EngramHandle { db })),
Err(_) => std::ptr::null_mut(),
}
}
/// Close and free an engram database handle.
///
/// After this call, `handle` is invalid and must not be used.
///
/// # Safety
/// `handle` must have been returned by `engram_open` and not yet freed.
#[no_mangle]
pub unsafe extern "C" fn engram_close(handle: *mut EngramHandle) {
if !handle.is_null() {
drop(Box::from_raw(handle));
}
}
/// Return the number of nodes in the database.
///
/// Returns -1 on error.
///
/// # Safety
/// `handle` must be a valid, non-null pointer from `engram_open`.
#[no_mangle]
pub unsafe extern "C" fn engram_node_count(handle: *const EngramHandle) -> i64 {
if handle.is_null() {
return -1;
}
match (*handle).db.node_count() {
Ok(n) => n as i64,
Err(_) => -1,
}
}
/// Return the number of edges in the database.
///
/// Returns -1 on error.
///
/// # Safety
/// `handle` must be a valid, non-null pointer from `engram_open`.
#[no_mangle]
pub unsafe extern "C" fn engram_edge_count(handle: *const EngramHandle) -> i64 {
if handle.is_null() {
return -1;
}
match (*handle).db.edge_count() {
Ok(n) => n as i64,
Err(_) => -1,
}
}
/// Apply salience decay across all nodes.
///
/// Returns the number of nodes updated, or -1 on error.
///
/// # Safety
/// `handle` must be a valid, non-null pointer from `engram_open`.
#[no_mangle]
pub unsafe extern "C" fn engram_decay(handle: *mut EngramHandle, factor: f32) -> i64 {
if handle.is_null() {
return -1;
}
match (*handle).db.decay(factor) {
Ok(n) => n as i64,
Err(_) => -1,
}
}
/// Free a C string returned by engram FFI functions.
///
/// # Safety
/// `s` must have been allocated by an engram FFI function, not by the caller.
#[no_mangle]
pub unsafe extern "C" fn engram_free_string(s: *mut c_char) {
if !s.is_null() {
drop(CString::from_raw(s));
}
}