mnemo-engine 0.4.0__tar.gz → 0.4.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. {mnemo_engine-0.4.0 → mnemo_engine-0.4.1}/PKG-INFO +12 -1
  2. {mnemo_engine-0.4.0 → mnemo_engine-0.4.1}/README.md +11 -0
  3. {mnemo_engine-0.4.0 → mnemo_engine-0.4.1}/mnemo/Cargo.lock +101 -4
  4. {mnemo_engine-0.4.0 → mnemo_engine-0.4.1}/mnemo/Cargo.toml +22 -2
  5. mnemo_engine-0.4.1/mnemo/deny.toml +131 -0
  6. mnemo_engine-0.4.1/mnemo/src/lib.rs +158 -0
  7. {mnemo_engine-0.4.0 → mnemo_engine-0.4.1}/mnemo/src/mcp.rs +11 -0
  8. {mnemo_engine-0.4.0 → mnemo_engine-0.4.1}/mnemo/src/pager.rs +72 -0
  9. {mnemo_engine-0.4.0 → mnemo_engine-0.4.1}/mnemo/src/wal.rs +25 -4
  10. mnemo_engine-0.4.1/mnemo/tests/proptest_store.proptest-regressions +8 -0
  11. mnemo_engine-0.4.1/mnemo/tests/proptest_store.rs +526 -0
  12. {mnemo_engine-0.4.0 → mnemo_engine-0.4.1}/mnemo-python/Cargo.lock +4 -4
  13. {mnemo_engine-0.4.0 → mnemo_engine-0.4.1}/mnemo-python/Cargo.toml +1 -1
  14. {mnemo_engine-0.4.0 → mnemo_engine-0.4.1}/mnemo-python/README.md +11 -0
  15. {mnemo_engine-0.4.0 → mnemo_engine-0.4.1}/mnemo-python/src/lib.rs +165 -5
  16. {mnemo_engine-0.4.0 → mnemo_engine-0.4.1}/mnemo-python/test_mnemo.py +171 -0
  17. {mnemo_engine-0.4.0 → mnemo_engine-0.4.1}/pyproject.toml +1 -1
  18. mnemo_engine-0.4.0/mnemo/src/lib.rs +0 -80
  19. {mnemo_engine-0.4.0 → mnemo_engine-0.4.1}/mnemo/.gitignore +0 -0
  20. {mnemo_engine-0.4.0 → mnemo_engine-0.4.1}/mnemo/README.md +0 -0
  21. {mnemo_engine-0.4.0 → mnemo_engine-0.4.1}/mnemo/examples/quickstart.rs +0 -0
  22. {mnemo_engine-0.4.0 → mnemo_engine-0.4.1}/mnemo/examples/session.rs +0 -0
  23. {mnemo_engine-0.4.0 → mnemo_engine-0.4.1}/mnemo/src/bin/mnemo.rs +0 -0
  24. {mnemo_engine-0.4.0 → mnemo_engine-0.4.1}/mnemo/src/cache.rs +0 -0
  25. {mnemo_engine-0.4.0 → mnemo_engine-0.4.1}/mnemo/src/crypto.rs +0 -0
  26. {mnemo_engine-0.4.0 → mnemo_engine-0.4.1}/mnemo/src/error.rs +0 -0
  27. {mnemo_engine-0.4.0 → mnemo_engine-0.4.1}/mnemo/src/format.rs +0 -0
  28. {mnemo_engine-0.4.0 → mnemo_engine-0.4.1}/mnemo/src/index.rs +0 -0
  29. {mnemo_engine-0.4.0 → mnemo_engine-0.4.1}/mnemo/src/memory.rs +0 -0
  30. {mnemo_engine-0.4.0 → mnemo_engine-0.4.1}/mnemo/src/result_cache.rs +0 -0
  31. {mnemo_engine-0.4.0 → mnemo_engine-0.4.1}/mnemo/src/session.rs +0 -0
  32. {mnemo_engine-0.4.0 → mnemo_engine-0.4.1}/mnemo/src/store.rs +0 -0
  33. {mnemo_engine-0.4.0 → mnemo_engine-0.4.1}/mnemo/tests/cli_smoke.rs +0 -0
  34. {mnemo_engine-0.4.0 → mnemo_engine-0.4.1}/mnemo/tests/integration.rs +0 -0
  35. {mnemo_engine-0.4.0 → mnemo_engine-0.4.1}/mnemo-python/.gitignore +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mnemo-engine
3
- Version: 0.4.0
3
+ Version: 0.4.1
4
4
  Classifier: Programming Language :: Rust
5
5
  Classifier: Programming Language :: Python :: 3
6
6
  Classifier: License :: OSI Approved :: Apache Software License
@@ -155,11 +155,22 @@ its own agent.
155
155
  | `cache_stats(namespace=None)` | Result-cache stats: `{entries, bytes, hits, misses, hit_rate, evictions}` |
156
156
  | `cache_put_semantic(namespace, key, vector, value, model, content_type="text", ttl_secs=None)` | Semantic cache put (Phase 10.2) — vector must match db dimensions |
157
157
  | `cache_get_semantic(namespace, query, model, threshold=0.97)` | Top-1 cosine over the namespace's vectored entries whose `model` matches |
158
+ | `set_cache_flush_policy(policy, max_dirty=None, max_age_secs=None)` | `policy="strict"` (default; every put durable on return) or `"batched"` (engine auto-flushes at `max_dirty` puts or `max_age_secs` seconds). Under `batched`, `cache_put` and friends no longer force an eager flush — call `db.flush()` yourself if you need it before a specific boundary |
159
+ | `set_cache_budget(namespace, max_entries, max_bytes)` | Per-namespace LRU cap (defaults 10 000 entries / 64 MiB). On the next `cache_put` the LRU is evicted until both limits are satisfied |
158
160
  | `set_max_snapshots(max)` | Override the snapshot-manifest retention cap (default 256; `0` disables) |
161
+ | `memories()` | Enumerate every live (non-deleted, non-expired) memory as a list of dicts |
162
+ | `dimensions()` | Embedding dimensionality this database was created with (also on `stats()["dimensions"]`) |
163
+ | `rekey(new_passphrase, fast=False)` | Change the passphrase. Re-derives the KEK and re-wraps the DEK; encrypted pages are not rewritten (O(1) in db size). `fast=True` uses the cheap-Argon2 test params — omit for production |
159
164
  | `stats()` | Summary statistics |
160
165
  | `export_encrypted(dest)` | Copy the (already-encrypted) file elsewhere |
161
166
  | `len(db)` | Live memory count |
162
167
 
168
+ Plus one module-level function:
169
+
170
+ | Function | Purpose |
171
+ |---|---|
172
+ | `mnemo.compact_file(path, passphrase)` | Rewrite the file out-of-place, dropping tombstoned and expired memories and reclaiming stale pages. Caller's own handle must be closed first. Returns `{"before": int, "after": int}` |
173
+
163
174
  `Session` methods: `add_turn(turn)`, `recall(query, top_k=10, memory_types=None)`,
164
175
  `close()`, `discard()`, `id()`, `agent()`, `turn_ids()`, `turn_count()`; also a
165
176
  context manager (exiting consolidates).
@@ -141,11 +141,22 @@ its own agent.
141
141
  | `cache_stats(namespace=None)` | Result-cache stats: `{entries, bytes, hits, misses, hit_rate, evictions}` |
142
142
  | `cache_put_semantic(namespace, key, vector, value, model, content_type="text", ttl_secs=None)` | Semantic cache put (Phase 10.2) — vector must match db dimensions |
143
143
  | `cache_get_semantic(namespace, query, model, threshold=0.97)` | Top-1 cosine over the namespace's vectored entries whose `model` matches |
144
+ | `set_cache_flush_policy(policy, max_dirty=None, max_age_secs=None)` | `policy="strict"` (default; every put durable on return) or `"batched"` (engine auto-flushes at `max_dirty` puts or `max_age_secs` seconds). Under `batched`, `cache_put` and friends no longer force an eager flush — call `db.flush()` yourself if you need it before a specific boundary |
145
+ | `set_cache_budget(namespace, max_entries, max_bytes)` | Per-namespace LRU cap (defaults 10 000 entries / 64 MiB). On the next `cache_put` the LRU is evicted until both limits are satisfied |
144
146
  | `set_max_snapshots(max)` | Override the snapshot-manifest retention cap (default 256; `0` disables) |
147
+ | `memories()` | Enumerate every live (non-deleted, non-expired) memory as a list of dicts |
148
+ | `dimensions()` | Embedding dimensionality this database was created with (also on `stats()["dimensions"]`) |
149
+ | `rekey(new_passphrase, fast=False)` | Change the passphrase. Re-derives the KEK and re-wraps the DEK; encrypted pages are not rewritten (O(1) in db size). `fast=True` uses the cheap-Argon2 test params — omit for production |
145
150
  | `stats()` | Summary statistics |
146
151
  | `export_encrypted(dest)` | Copy the (already-encrypted) file elsewhere |
147
152
  | `len(db)` | Live memory count |
148
153
 
154
+ Plus one module-level function:
155
+
156
+ | Function | Purpose |
157
+ |---|---|
158
+ | `mnemo.compact_file(path, passphrase)` | Rewrite the file out-of-place, dropping tombstoned and expired memories and reclaiming stale pages. Caller's own handle must be closed first. Returns `{"before": int, "after": int}` |
159
+
149
160
  `Session` methods: `add_turn(turn)`, `recall(query, top_k=10, memory_types=None)`,
150
161
  `close()`, `discard()`, `id()`, `agent()`, `turn_ids()`, `turn_count()`; also a
151
162
  context manager (exiting consolidates).
@@ -120,6 +120,21 @@ version = "1.6.0"
120
120
  source = "registry+https://github.com/rust-lang/crates.io-index"
121
121
  checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
122
122
 
123
+ [[package]]
124
+ name = "bit-set"
125
+ version = "0.5.3"
126
+ source = "registry+https://github.com/rust-lang/crates.io-index"
127
+ checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1"
128
+ dependencies = [
129
+ "bit-vec",
130
+ ]
131
+
132
+ [[package]]
133
+ name = "bit-vec"
134
+ version = "0.6.3"
135
+ source = "registry+https://github.com/rust-lang/crates.io-index"
136
+ checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
137
+
123
138
  [[package]]
124
139
  name = "bitflags"
125
140
  version = "2.11.1"
@@ -293,6 +308,12 @@ version = "2.4.1"
293
308
  source = "registry+https://github.com/rust-lang/crates.io-index"
294
309
  checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6"
295
310
 
311
+ [[package]]
312
+ name = "fnv"
313
+ version = "1.0.7"
314
+ source = "registry+https://github.com/rust-lang/crates.io-index"
315
+ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
316
+
296
317
  [[package]]
297
318
  name = "fs4"
298
319
  version = "0.9.1"
@@ -399,6 +420,12 @@ dependencies = [
399
420
  "wasm-bindgen",
400
421
  ]
401
422
 
423
+ [[package]]
424
+ name = "lazy_static"
425
+ version = "1.5.0"
426
+ source = "registry+https://github.com/rust-lang/crates.io-index"
427
+ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
428
+
402
429
  [[package]]
403
430
  name = "libc"
404
431
  version = "0.2.186"
@@ -415,6 +442,12 @@ dependencies = [
415
442
  "windows-link",
416
443
  ]
417
444
 
445
+ [[package]]
446
+ name = "libm"
447
+ version = "0.2.16"
448
+ source = "registry+https://github.com/rust-lang/crates.io-index"
449
+ checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981"
450
+
418
451
  [[package]]
419
452
  name = "linux-raw-sys"
420
453
  version = "0.4.15"
@@ -429,13 +462,14 @@ checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
429
462
 
430
463
  [[package]]
431
464
  name = "mnemo-engine"
432
- version = "0.4.0"
465
+ version = "0.4.1"
433
466
  dependencies = [
434
467
  "aes-gcm",
435
468
  "argon2",
436
469
  "base64ct",
437
470
  "clap",
438
471
  "fs4",
472
+ "proptest",
439
473
  "rand",
440
474
  "rmp",
441
475
  "rmp-serde",
@@ -451,7 +485,7 @@ dependencies = [
451
485
 
452
486
  [[package]]
453
487
  name = "mnemo-node"
454
- version = "0.4.0"
488
+ version = "0.4.1"
455
489
  dependencies = [
456
490
  "mnemo-engine",
457
491
  "napi",
@@ -523,6 +557,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
523
557
  checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
524
558
  dependencies = [
525
559
  "autocfg",
560
+ "libm",
526
561
  ]
527
562
 
528
563
  [[package]]
@@ -596,6 +631,32 @@ dependencies = [
596
631
  "unicode-ident",
597
632
  ]
598
633
 
634
+ [[package]]
635
+ name = "proptest"
636
+ version = "1.5.0"
637
+ source = "registry+https://github.com/rust-lang/crates.io-index"
638
+ checksum = "b4c2511913b88df1637da85cc8d96ec8e43a3f8bb8ccb71ee1ac240d6f3df58d"
639
+ dependencies = [
640
+ "bit-set",
641
+ "bit-vec",
642
+ "bitflags",
643
+ "lazy_static",
644
+ "num-traits",
645
+ "rand",
646
+ "rand_chacha",
647
+ "rand_xorshift",
648
+ "regex-syntax",
649
+ "rusty-fork",
650
+ "tempfile",
651
+ "unarray",
652
+ ]
653
+
654
+ [[package]]
655
+ name = "quick-error"
656
+ version = "1.2.3"
657
+ source = "registry+https://github.com/rust-lang/crates.io-index"
658
+ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
659
+
599
660
  [[package]]
600
661
  name = "quote"
601
662
  version = "1.0.45"
@@ -607,9 +668,9 @@ dependencies = [
607
668
 
608
669
  [[package]]
609
670
  name = "rand"
610
- version = "0.8.5"
671
+ version = "0.8.6"
611
672
  source = "registry+https://github.com/rust-lang/crates.io-index"
612
- checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
673
+ checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a"
613
674
  dependencies = [
614
675
  "libc",
615
676
  "rand_chacha",
@@ -635,6 +696,15 @@ dependencies = [
635
696
  "getrandom",
636
697
  ]
637
698
 
699
+ [[package]]
700
+ name = "rand_xorshift"
701
+ version = "0.3.0"
702
+ source = "registry+https://github.com/rust-lang/crates.io-index"
703
+ checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f"
704
+ dependencies = [
705
+ "rand_core",
706
+ ]
707
+
638
708
  [[package]]
639
709
  name = "regex"
640
710
  version = "1.12.3"
@@ -726,6 +796,18 @@ version = "1.0.22"
726
796
  source = "registry+https://github.com/rust-lang/crates.io-index"
727
797
  checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
728
798
 
799
+ [[package]]
800
+ name = "rusty-fork"
801
+ version = "0.3.1"
802
+ source = "registry+https://github.com/rust-lang/crates.io-index"
803
+ checksum = "cc6bf79ff24e648f6da1f8d1f011e9cac26491b619e6b9280f2b47f1774e6ee2"
804
+ dependencies = [
805
+ "fnv",
806
+ "quick-error",
807
+ "tempfile",
808
+ "wait-timeout",
809
+ ]
810
+
729
811
  [[package]]
730
812
  name = "ryu"
731
813
  version = "1.0.23"
@@ -860,6 +942,12 @@ dependencies = [
860
942
  "web-time",
861
943
  ]
862
944
 
945
+ [[package]]
946
+ name = "unarray"
947
+ version = "0.1.4"
948
+ source = "registry+https://github.com/rust-lang/crates.io-index"
949
+ checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94"
950
+
863
951
  [[package]]
864
952
  name = "unicode-ident"
865
953
  version = "1.0.24"
@@ -894,6 +982,15 @@ version = "0.9.5"
894
982
  source = "registry+https://github.com/rust-lang/crates.io-index"
895
983
  checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
896
984
 
985
+ [[package]]
986
+ name = "wait-timeout"
987
+ version = "0.2.1"
988
+ source = "registry+https://github.com/rust-lang/crates.io-index"
989
+ checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11"
990
+ dependencies = [
991
+ "libc",
992
+ ]
993
+
897
994
  [[package]]
898
995
  name = "wasi"
899
996
  version = "0.11.1+wasi-snapshot-preview1"
@@ -6,7 +6,7 @@
6
6
  # below stay `mnemo`, so consumers still write `use mnemo::...` and invoke
7
7
  # `mnemo` on the command line after `cargo add mnemo-engine`.
8
8
  name = "mnemo-engine"
9
- version = "0.4.0"
9
+ version = "0.4.1"
10
10
  edition = "2021"
11
11
  rust-version = "1.75"
12
12
  description = "Encrypted, single-file, portable agent memory engine with multi-signal recall, sessions, snapshots, and an IVF+PQ index"
@@ -55,7 +55,14 @@ base64ct = "=1.6.0"
55
55
  ulid = { version = "=1.1.3", features = ["serde"] }
56
56
  thiserror = "=1.0.64"
57
57
  zeroize = "=1.8.1"
58
- rand = "=0.8.5"
58
+ # Pinned `=0.8.6` (patch bump from 0.8.5) to close RUSTSEC-2026-0097,
59
+ # an unsoundness that only triggers under a very narrow config —
60
+ # custom `log::Log` impl reentrantly calling `rand::rng()` on
61
+ # `ThreadRng` mid-reseed — which mnemo does not do. The engine
62
+ # never touches `log`, and no CLI code paths reach `ThreadRng` from
63
+ # a logger. Upgrading anyway is the least-surprising thing to do
64
+ # and satisfies the `advisories` check without an ignore.
65
+ rand = "=0.8.6"
59
66
  clap = { version = "=4.4.18", features = ["derive"] }
60
67
  # Used by the `mnemo` CLI binary only — `passphrase()` calls it when stdin
61
68
  # is a TTY to read the passphrase without echo. The library never touches
@@ -77,6 +84,19 @@ sha2 = "=0.10.8"
77
84
 
78
85
  [dev-dependencies]
79
86
  tempfile = "=3.10.1"
87
+ # Property-based testing (Phase 1.3). Pinned exactly; `=1.5.0`'s MSRV
88
+ # is Rust 1.65, well under our 1.75 floor. Kept as a dev-dep only —
89
+ # it does not appear in the shipped library dependency set.
90
+ proptest = "=1.5.0"
91
+
92
+ [features]
93
+ # When enabled, exposes crash-injection hooks used by the Phase 1.3
94
+ # proptest under `mnemo::__failpoints::*`. Always enabled during
95
+ # `cargo test` via `#[cfg(test)]`; the feature is here so an external
96
+ # fault-injection consumer (or a future integration test in a sibling
97
+ # crate) can opt into the same hooks without touching this crate's
98
+ # tests. Not for production use — never enable in a release build.
99
+ failpoints = []
80
100
 
81
101
  [profile.release]
82
102
  lto = true
@@ -0,0 +1,131 @@
1
+ # cargo-deny configuration (Phase 1.4).
2
+ #
3
+ # Runs in CI on every PR via `cargo deny check` in `mnemo/`. Three
4
+ # checks matter here:
5
+ # 1. `advisories` — cross-references the RustSec advisory DB and
6
+ # fails on any unpatched vulnerability affecting a dep in the
7
+ # lockfile.
8
+ # 2. `licenses` — allowlist of OSI-approved licenses compatible
9
+ # with the Apache-2.0 the mnemo crate itself is published under.
10
+ # Anything unlisted requires an explicit `exceptions` entry.
11
+ # 3. `bans` — currently informational (`multiple-versions = "warn"`)
12
+ # to catch transitive duplication without blocking merges while
13
+ # we enumerate the necessary skip set. Follow-up PR will
14
+ # tighten to `deny` once CI has surfaced the real exception
15
+ # list.
16
+ #
17
+ # Run locally with:
18
+ # cd mnemo && cargo deny check
19
+ #
20
+ # For just one check:
21
+ # cargo deny check advisories
22
+ # cargo deny check licenses
23
+ # cargo deny check bans
24
+
25
+ # ---------------------------------------------------------------------
26
+ # Graph configuration — what crates the checks apply to.
27
+ # ---------------------------------------------------------------------
28
+
29
+ [graph]
30
+ # Empty `targets` means "check all crates for all target triples the
31
+ # lockfile references." Since mnemo compiles on Linux/macOS/Windows
32
+ # x86_64+aarch64, restricting to a single target would hide license /
33
+ # advisory hits on platform-specific transitives (e.g. `windows-sys`
34
+ # on Windows only).
35
+ targets = []
36
+ all-features = false
37
+ no-default-features = false
38
+
39
+ [output]
40
+ # Group violations by feature depth; keeps the diagnostic readable
41
+ # on the multi-page transitive graph typical of a Rust crate.
42
+ feature-depth = 1
43
+
44
+ # ---------------------------------------------------------------------
45
+ # Advisory DB — RustSec vulnerability + yanked-crate check.
46
+ # ---------------------------------------------------------------------
47
+
48
+ [advisories]
49
+ version = 2
50
+ db-path = "~/.cargo/advisory-db"
51
+ db-urls = ["https://github.com/rustsec/advisory-db"]
52
+ # `yanked` fires when Cargo.lock pins a version the author later
53
+ # yanked from crates.io. `warn` first so we notice; upgrade to
54
+ # `deny` once we've picked up a clean baseline.
55
+ yanked = "warn"
56
+ # Time-boxed / justified exceptions to the advisory check. Every
57
+ # entry must carry a `reason` — a future worker reading this file
58
+ # should be able to tell why the ignore was granted without leaving
59
+ # the config.
60
+ ignore = [
61
+ # `paste 1.0.15` — dtolnay archived the repo (RUSTSEC-2024-0436,
62
+ # "unmaintained"). NOT a vulnerability. It is pulled transitively
63
+ # by `rmp` / `rmp-serde` (our MessagePack layer) which have no
64
+ # migration path today. The advisory itself suggests `pastey` as
65
+ # a drop-in fork; that migration lives in `rmp`, not here. Revisit
66
+ # when `rmp-serde` ships a release that swaps to `pastey`.
67
+ { id = "RUSTSEC-2024-0436", reason = "paste (unmaintained) is a transitive dep of rmp-serde; no upstream migration path yet — track via rmp-serde issue tracker" },
68
+ ]
69
+
70
+ # ---------------------------------------------------------------------
71
+ # Licenses — SPDX allowlist. Anything not listed → build fails.
72
+ # ---------------------------------------------------------------------
73
+
74
+ [licenses]
75
+ version = 2
76
+ # Kept minimal — the mnemo dep graph in practice only ships crates
77
+ # under Apache-2.0, MIT, BSD-3-Clause, and Unicode-3.0 (the newer
78
+ # SPDX variant `unicode-ident` uses). Apache-2.0 WITH LLVM-exception
79
+ # covers the `rustix` family. Adding an unused allow is not a security
80
+ # hole but it's noise — cargo-deny prints an "unmatched license
81
+ # allowance" warning for each, and drift in the allowlist becomes
82
+ # hard to notice. Extend this list only when a new dep actually
83
+ # ships under something not already here.
84
+ allow = [
85
+ "Apache-2.0",
86
+ "Apache-2.0 WITH LLVM-exception",
87
+ "MIT",
88
+ "BSD-2-Clause",
89
+ "BSD-3-Clause",
90
+ "Unicode-3.0",
91
+ ]
92
+ # Threshold for guessing a license from a LICENSE file's text when
93
+ # the crate omits SPDX metadata. Default 0.8 is conservative — lower
94
+ # it only if a specific crate needs a `[[licenses.clarify]]` entry.
95
+ confidence-threshold = 0.8
96
+ # Per-crate license overrides go here if a crate ships an unusual
97
+ # LICENSE mix cargo-deny can't parse. Empty at PR 9 landing;
98
+ # expand as CI surfaces cases.
99
+ exceptions = []
100
+
101
+ # ---------------------------------------------------------------------
102
+ # Bans — duplicate versions, wildcard deps, per-crate allow/deny.
103
+ # ---------------------------------------------------------------------
104
+
105
+ [bans]
106
+ # `warn` for now: transitive graphs commonly duplicate `syn 1/2`,
107
+ # `regex-syntax 0.6/0.8`, `bitflags 1/2`, `windows-sys 0.4x/0.5x`
108
+ # during migrations. First green CI run will list the actual
109
+ # duplicates; follow-up PR flips to `deny` with the enumerated
110
+ # `skip` entries.
111
+ multiple-versions = "warn"
112
+ wildcards = "allow"
113
+ highlight = "all"
114
+ workspace-default-features = "allow"
115
+ external-default-features = "allow"
116
+ allow = []
117
+ deny = []
118
+ skip = []
119
+ skip-tree = []
120
+
121
+ # ---------------------------------------------------------------------
122
+ # Sources — where crates may come from.
123
+ # ---------------------------------------------------------------------
124
+
125
+ [sources]
126
+ unknown-registry = "warn"
127
+ unknown-git = "warn"
128
+ # Only crates.io. No git deps in mnemo (all pinned to `=x.y.z` per
129
+ # AGENTS.md), so this stays tight.
130
+ allow-registry = ["https://github.com/rust-lang/crates.io-index"]
131
+ allow-git = []
@@ -0,0 +1,158 @@
1
+ //! # Mnemo
2
+ //!
3
+ //! Mnemo is an **encrypted, single-file, portable agent-memory engine**.
4
+ //!
5
+ //! A whole memory store — vectors, content, metadata, and the multi-signal
6
+ //! recall machinery an agent needs — lives in one file you can copy, back up,
7
+ //! or hand to another process. The file is encrypted at rest with a two-tier
8
+ //! key hierarchy: an Argon2id key-encryption key (KEK) derived from a
9
+ //! passphrase wraps a random data-encryption key (DEK), and the DEK encrypts
10
+ //! every page with AES-256-GCM.
11
+ //!
12
+ //! ## Quick start
13
+ //!
14
+ //! ```no_run
15
+ //! use mnemo::{Mnemo, MnemoConfig, Memory, MemoryType, RecallRequest};
16
+ //!
17
+ //! # fn main() -> mnemo::Result<()> {
18
+ //! let cfg = MnemoConfig { dimensions: 3, ..Default::default() };
19
+ //! let mut db = Mnemo::create("agent.mnemo", "correct horse battery", cfg)?;
20
+ //!
21
+ //! db.remember(
22
+ //! Memory::new("the user prefers dark mode", MemoryType::Semantic, vec![0.1, 0.2, 0.9])
23
+ //! .with_agent("assistant-1")
24
+ //! .with_importance(0.8),
25
+ //! )?;
26
+ //! db.flush()?;
27
+ //!
28
+ //! let hits = db.recall(&RecallRequest::new(vec![0.1, 0.2, 0.9]).top_k(5))?;
29
+ //! for h in hits {
30
+ //! println!("{:.3} {}", h.score, h.memory.content);
31
+ //! }
32
+ //! # Ok(())
33
+ //! # }
34
+ //! ```
35
+ //!
36
+ //! ## What is and is not built
37
+ //!
38
+ //! This crate implements a real, tested core: the encrypted single-file
39
+ //! storage engine, the crypto layer, the agent-memory model, multi-signal
40
+ //! recall, an IVF+PQ approximate-nearest-neighbour index, a write-ahead log,
41
+ //! snapshot-based point-in-time recovery, a bounded LRU page cache, and the
42
+ //! [`Session`] conversation wrapper. Exact brute-force search remains
43
+ //! available as the ground-truth baseline; a built index makes
44
+ //! [`Mnemo::recall`] sub-linear. Each [`Mnemo::flush`] is one atomic,
45
+ //! WAL-committed transaction and a restorable snapshot — [`Mnemo::restore_to`]
46
+ //! rewinds the database to any past transaction. Python and TypeScript
47
+ //! language bindings are the one documented roadmap item — see the README.
48
+
49
+ #![forbid(unsafe_code)]
50
+ #![warn(missing_docs)]
51
+
52
+ mod cache;
53
+ mod crypto;
54
+ mod error;
55
+ mod format;
56
+ mod index;
57
+ pub mod mcp;
58
+ mod memory;
59
+ mod pager;
60
+ mod result_cache;
61
+ mod session;
62
+ mod store;
63
+ mod wal;
64
+
65
+ pub use crypto::KdfParams;
66
+ pub use error::{MnemoError, Result};
67
+ pub use index::{IndexConfig, IndexInfo};
68
+ pub use memory::{Memory, MemoryType, Metric, Scope, ScoreWeights};
69
+ pub use result_cache::{
70
+ CacheBudget, CacheFlushPolicy, CachePutOpts, CacheStats, CachedValue, SemanticCachePutOpts,
71
+ DEFAULT_BATCH_MAX_AGE, DEFAULT_BATCH_MAX_DIRTY, DEFAULT_MAX_BYTES, DEFAULT_MAX_ENTRIES,
72
+ DEFAULT_SEMANTIC_THRESHOLD,
73
+ };
74
+ pub use session::{Role, Session, Turn};
75
+ pub use store::{
76
+ CompactReport, Mnemo, MnemoConfig, RecallRequest, RecallResult, SnapshotInfo, Stats,
77
+ };
78
+
79
+ /// Re-export of [`ulid::Ulid`], the identifier type used for memories.
80
+ pub use ulid::Ulid;
81
+
82
+ /// Test-only crash-injection surface used by the Phase 1.3 proptest
83
+ /// (`mnemo/tests/proptest_store.rs`). Compiled in under `cfg(test)`
84
+ /// and the `failpoints` feature only; absent from release builds.
85
+ ///
86
+ /// **Not part of the stable public API.** The double-underscore
87
+ /// convention makes the "internal" contract obvious at the call site.
88
+ #[cfg(any(test, feature = "failpoints"))]
89
+ #[doc(hidden)]
90
+ pub mod __failpoints {
91
+ /// Arm the pager and WAL commit path to fail on the Nth
92
+ /// subsequent write. `n = 0` fails the very next write; `n = -1`
93
+ /// (or any negative) disables. One-shot — disarms on trigger.
94
+ ///
95
+ /// Every write is checked before the byte hits the file, so a
96
+ /// triggered failure is guaranteed torn-BEFORE, never
97
+ /// torn-AFTER — critical for the "either pre-flush or post-flush"
98
+ /// invariant the property test asserts.
99
+ pub fn set_writes_until_fail(n: i64) {
100
+ crate::pager::failpoints::set_writes_until_fail(n)
101
+ }
102
+ }
103
+
104
+ /// Internal parse surfaces exposed for cargo-fuzz targets (Phase 1.2).
105
+ ///
106
+ /// **Not part of the stable public API.** Each function feeds an
107
+ /// arbitrary byte slice into a parse/decode path that a hostile
108
+ /// `.mnemo` file (or a hostile MCP client) can send us, and returns
109
+ /// `Result<()>` so a fuzz target only asserts "no panic." Marked
110
+ /// `#[doc(hidden)]` so `cargo doc` doesn't advertise it; do not import
111
+ /// from application code.
112
+ #[doc(hidden)]
113
+ pub mod __fuzz {
114
+ /// Parse a `.mnemo` header page (page 0, plaintext) from arbitrary
115
+ /// bytes. This is the pre-passphrase attack surface — a hostile
116
+ /// file hits it before any DEK exists.
117
+ pub fn parse_header(bytes: &[u8]) -> crate::Result<()> {
118
+ crate::format::Header::from_page(bytes).map(|_| ())
119
+ }
120
+
121
+ /// Replay the WAL scan over an arbitrary byte region. Delegates
122
+ /// to [`crate::wal::recover_bytes`]; a corrupt / truncated /
123
+ /// adversarial region must return `Err` or `Ok(None/Some(_))`,
124
+ /// never panic.
125
+ pub fn wal_recover_bytes(region: &[u8], wal_seq: u64) -> crate::Result<()> {
126
+ crate::wal::recover_bytes(region, wal_seq).map(|_| ())
127
+ }
128
+
129
+ /// Decode a `Memory` record body from arbitrary bytes.
130
+ pub fn decode_memory(bytes: &[u8]) -> crate::Result<()> {
131
+ rmp_serde::from_slice::<crate::Memory>(bytes)
132
+ .map(|_| ())
133
+ .map_err(|e| crate::MnemoError::Serialize(e.to_string()))
134
+ }
135
+
136
+ /// Decode a `CacheEntry` record body from arbitrary bytes. Type is
137
+ /// `pub(crate)`, so this wrapper hides it behind a unit return.
138
+ pub fn decode_cache_entry(bytes: &[u8]) -> crate::Result<()> {
139
+ rmp_serde::from_slice::<crate::result_cache::CacheEntry>(bytes)
140
+ .map(|_| ())
141
+ .map_err(|e| crate::MnemoError::Serialize(e.to_string()))
142
+ }
143
+
144
+ /// Decode a cache-directory page-run body (a `Vec<CacheDirectoryEntry>`)
145
+ /// from arbitrary bytes.
146
+ pub fn decode_cache_directory(bytes: &[u8]) -> crate::Result<()> {
147
+ rmp_serde::from_slice::<Vec<crate::result_cache::CacheDirectoryEntry>>(bytes)
148
+ .map(|_| ())
149
+ .map_err(|e| crate::MnemoError::Serialize(e.to_string()))
150
+ }
151
+
152
+ /// Parse a single MCP stdio JSON-RPC request line. Malformed
153
+ /// input returns `Err(String)`; the fuzz target only asserts
154
+ /// no panic and that the server would not die on this line.
155
+ pub fn parse_mcp_request_line(line: &str) -> Result<(), String> {
156
+ crate::mcp::__fuzz_parse_request_line(line)
157
+ }
158
+ }
@@ -60,6 +60,17 @@ const PROTOCOL_VERSION: &str = "2025-06-18";
60
60
  /// Environment variable that supplies the database passphrase.
61
61
  const PASSPHRASE_ENV: &str = "MNEMO_PASSPHRASE";
62
62
 
63
+ /// Fuzz-target helper: parse a single JSON-RPC line as a `Request` and
64
+ /// discard. Panic-free — malformed input returns `Err(String)`, valid
65
+ /// requests return `Ok(())`. Exposed only via `crate::__fuzz`; not part
66
+ /// of the stable public API.
67
+ #[doc(hidden)]
68
+ pub fn __fuzz_parse_request_line(line: &str) -> std::result::Result<(), String> {
69
+ serde_json::from_str::<Request>(line)
70
+ .map(|_| ())
71
+ .map_err(|e| e.to_string())
72
+ }
73
+
63
74
  // --- JSON-RPC 2.0 wire types ---------------------------------------------
64
75
 
65
76
  /// Incoming JSON-RPC 2.0 request or notification (a notification is a