velesdb-memory 0.15.0

VelesDB-memory: local-first MCP memory server for AI agents (remember/recall/relate/forget/why + deterministic context compiler).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
//! The shared atomic-replace primitives: each check names the store that
//! called it in its message and refuses a symlink or a path of the wrong
//! kind, `path_exists` does not follow links, and `promote` replaces a file
//! whole.

use std::fs;

#[cfg(unix)]
use super::open_regular_file_allow_hard_links;
use super::{open_regular_file, path_exists, promote, validate_regular_file, validate_workspace};

/// The two stores that share these primitives each name themselves in every
/// message, so each check runs under both names.
const ENTITIES: [&str; 2] = ["controller", "online migration"];

#[test]
fn a_workspace_must_be_a_real_directory_and_says_so_under_each_stores_name() {
    let root = tempfile::tempdir().expect("root");
    let file = root.path().join("file");
    fs::write(&file, b"x").expect("file");
    for entity in ENTITIES {
        validate_workspace(root.path(), entity).expect("a real directory");

        let refused = validate_workspace(&file, entity).expect_err("a file is no workspace");
        assert!(
            refused
                .to_string()
                .contains(&format!("{entity} workspace must be a real directory")),
            "{refused}"
        );
        let missing = validate_workspace(&root.path().join("absent"), entity).expect_err("absent");
        assert!(
            missing
                .to_string()
                .contains(&format!("cannot inspect {entity} workspace: ")),
            "{missing}"
        );
    }
}

#[test]
fn a_file_must_be_a_regular_file_and_says_so_under_each_stores_name() {
    let root = tempfile::tempdir().expect("root");
    let file = root.path().join("file");
    fs::write(&file, b"x").expect("file");
    for entity in ENTITIES {
        validate_regular_file(&file, entity).expect("a regular file");

        let refused = validate_regular_file(root.path(), entity).expect_err("a directory");
        assert!(
            refused
                .to_string()
                .contains(&format!("{entity} path must be a regular file")),
            "{refused}"
        );
        let missing =
            validate_regular_file(&root.path().join("absent"), entity).expect_err("absent");
        assert!(
            missing
                .to_string()
                .contains(&format!("cannot inspect {entity} file: ")),
            "{missing}"
        );
    }
}

#[cfg(unix)]
#[test]
fn a_symlink_is_refused_as_a_workspace_and_as_a_file() {
    use std::os::unix::fs::symlink;

    let root = tempfile::tempdir().expect("root");
    let real_dir = root.path().join("real");
    fs::create_dir(&real_dir).expect("dir");
    let victim = root.path().join("victim");
    fs::write(&victim, b"untouched").expect("victim");
    let dir_link = root.path().join("dir-link");
    let file_link = root.path().join("file-link");
    symlink(&real_dir, &dir_link).expect("dir link");
    symlink(&victim, &file_link).expect("file link");

    for entity in ENTITIES {
        let workspace = validate_workspace(&dir_link, entity).expect_err("linked workspace");
        assert!(
            workspace
                .to_string()
                .contains(&format!("{entity} workspace must be a real directory")),
            "{workspace}"
        );
        let file = validate_regular_file(&file_link, entity).expect_err("linked file");
        assert!(
            file.to_string()
                .contains(&format!("{entity} path must be a regular file")),
            "{file}"
        );
    }
    assert_eq!(fs::read(&victim).expect("victim"), b"untouched");
}

#[test]
fn open_regular_file_reads_and_writes_a_real_file() {
    let root = tempfile::tempdir().expect("root");
    let path = root.path().join("state");
    fs::write(&path, b"first").expect("seed");

    let mut options = fs::OpenOptions::new();
    options.read(true).write(true);
    let mut file = open_regular_file(&path, "test", options).expect("a regular file opens");
    let mut read = Vec::new();
    std::io::Read::read_to_end(&mut file, &mut read).expect("read");
    assert_eq!(read, b"first");
}

#[cfg(unix)]
#[test]
fn open_regular_file_refuses_a_symlink_and_says_so_under_each_stores_name() {
    use std::os::unix::fs::symlink;

    let root = tempfile::tempdir().expect("root");
    let victim = root.path().join("victim");
    fs::write(&victim, b"untouched").expect("victim");
    let link = root.path().join("link");
    symlink(&victim, &link).expect("link");

    for entity in ENTITIES {
        let mut options = fs::OpenOptions::new();
        options.read(true);
        let refused = open_regular_file(&link, entity, options).expect_err("a symlink");
        assert!(
            refused
                .to_string()
                .contains(&format!("{entity} path must be a regular file")),
            "{refused}"
        );
    }
    assert_eq!(fs::read(&victim).expect("victim"), b"untouched");
}

/// #2404: `validate_regular_file` followed by a plain `open` were two
/// separate syscalls against the *path*, with a window between them for a
/// symlink to be swapped in. This test does not, and cannot, exercise that
/// window from a single thread — it swaps the link in before calling
/// `open_regular_file` at all, which the old two-step code would have caught
/// too if the swap landed before its own first step. What it does pin: a
/// symlink present at call time is refused, and, unlike the two-step code,
/// `open_regular_file` has no separate first step for a check to pass and a
/// later step to race against — the one check it makes is `fstat` on the
/// file descriptor `open(O_NOFOLLOW)` already returned, not a second `stat`
/// of the path, so there is no second syscall left to land a swap between.
/// A mutant that reverts this function to `validate_regular_file(path,
/// entity)?; options.open(path)` still passes this specific test (the swap
/// is already in place before either step runs), and no other test in this
/// file catches it either — two attempts at a concurrent swap-under-load
/// test were tried and dropped: neither reliably won a kernel-level race
/// that is only a couple of syscalls wide (measured kill rates from ~3% to
/// ~55% across designs and runs, nowhere near a bound a CI gate could rely
/// on). That mutant is refused by construction, not by a test: `stat` then
/// `open` are two operations on the *path*, so anything can happen to the
/// path between them; `open(O_NOFOLLOW)` then `fstat` on the file
/// descriptor it returns are two operations on the *same already-opened
/// file*, and nothing done to the path afterward can change which file
/// that descriptor points to. Forcing and observing the first kind of race
/// deterministically from a plain unit test — reliably enough to gate a
/// merge on it — needs syscall-level interposition (`ptrace`, a seccomp
/// user-space notifier, or an `LD_PRELOAD` shim), which is out of
/// proportion for this fix.
#[cfg(unix)]
#[test]
fn open_regular_file_refuses_a_symlink_present_at_call_time_with_no_check_step_of_its_own() {
    use std::os::unix::fs::symlink;

    let root = tempfile::tempdir().expect("root");
    let victim = root.path().join("victim");
    fs::write(&victim, b"untouched").expect("victim");
    let path = root.path().join("state");
    fs::write(&path, b"regular").expect("a real file first");

    // What a prior `validate_regular_file(&path, ..)` check would have seen:
    // a regular file. The swap happens right after, before the only
    // remaining step (open_regular_file's single open+fstat).
    fs::remove_file(&path).expect("remove the regular file");
    symlink(&victim, &path).expect("swap in a link to the victim");

    let mut options = fs::OpenOptions::new();
    options.read(true);
    let refused = open_regular_file(&path, "test", options).expect_err("swapped-in symlink");
    assert!(
        refused
            .to_string()
            .contains("test path must be a regular file"),
        "{refused}"
    );
    assert_eq!(fs::read(&victim).expect("victim"), b"untouched");
}

/// A planted FIFO must be refused promptly, not hang the caller. Opening a
/// FIFO for read blocks until a writer opens the other end; without
/// `O_NONBLOCK` this call would wait forever instead of reaching the
/// "not a regular file" refusal below (a mutant that drops `O_NONBLOCK`
/// times out this test rather than failing it cleanly).
#[cfg(unix)]
#[test]
fn open_regular_file_refuses_a_fifo_without_blocking() {
    let root = tempfile::tempdir().expect("root");
    let fifo = root.path().join("pipe");
    let status = std::process::Command::new("mkfifo")
        .arg(&fifo)
        .status()
        .expect("test: run mkfifo");
    assert!(status.success(), "mkfifo failed: {status}");

    let (tx, rx) = std::sync::mpsc::channel();
    let path = fifo.clone();
    std::thread::spawn(move || {
        let mut options = fs::OpenOptions::new();
        options.read(true);
        let result = open_regular_file(&path, "test", options).map(|_| ());
        let _ = tx.send(result.map_err(|err| err.to_string()));
    });

    let refused = rx
        .recv_timeout(std::time::Duration::from_secs(5))
        .expect("open_regular_file must return instead of blocking on the FIFO")
        .expect_err("a FIFO is not a regular file");
    assert!(
        refused.contains("test path must be a regular file"),
        "{refused}"
    );
}

/// #2407 (round 5): a hard link to a file outside the store is,
/// structurally, an ordinary regular file, so `is_file` alone accepts it —
/// but `fstat` also reports `nlink`, which this check refuses above 1.
#[cfg(unix)]
#[test]
fn open_regular_file_refuses_a_hard_link() {
    let root = tempfile::tempdir().expect("root");
    let victim = root.path().join("victim");
    fs::write(&victim, b"untouched").expect("victim");
    let path = root.path().join("state");
    fs::hard_link(&victim, &path).expect("hard link");

    let mut options = fs::OpenOptions::new();
    options.read(true);
    let refused = open_regular_file(&path, "test", options).expect_err("a hard link");
    assert!(
        refused
            .to_string()
            .contains("test path must be a regular file"),
        "{refused}"
    );
    assert_eq!(fs::read(&victim).expect("victim"), b"untouched");
}

/// #2409 round 10: `nlink > 1` (not `!= 1`) is the refusal, specifically so
/// a reader racing a legitimate rename-replace of the same path — which
/// transiently drops the OLD inode's `nlink` to 0 on a handle a reader
/// already has open — is never refused. `controller/state.rs` and
/// `online_migration/job_state.rs` are exactly this shape (`write_synced` to
/// a staging file, then `promote`), and this race had no direct test:
/// round 7 moved the extraction-job-record call site this PR originally
/// cited off `open_regular_file` entirely, so the two stress-tested
/// extraction-recovery tests no longer exercise this guard at all. Proven
/// by mutation: reverting to `nlink() != 1` makes this test fail (observed
/// on macOS: roughly 1,700 of 20,000 iterations, about 8-9%, varying by
/// run and machine load).
#[cfg(unix)]
#[test]
fn open_regular_file_never_refuses_a_legitimate_rename_replace_race() {
    use std::sync::atomic::{AtomicUsize, Ordering};

    const ITERATIONS: usize = 20_000;

    let root = tempfile::tempdir().expect("root");
    let path = root.path().join("state");
    let staging = root.path().join("state.tmp");
    fs::write(&path, b"v0").expect("seed");

    let spurious_refusals = AtomicUsize::new(0);

    std::thread::scope(|scope| {
        scope.spawn(|| {
            for i in 0..ITERATIONS {
                fs::write(&staging, format!("v{i}").as_bytes()).expect("write staging");
                promote(&staging, &path).expect("promote");
            }
        });

        for _ in 0..ITERATIONS {
            let mut options = fs::OpenOptions::new();
            options.read(true);
            if open_regular_file(&path, "test", options).is_err() {
                spurious_refusals.fetch_add(1, Ordering::Relaxed);
            }
        }
    });

    assert_eq!(
        spurious_refusals.load(Ordering::Relaxed),
        0,
        "a reader racing a legitimate rename-replace must never be refused as a hard link"
    );
}

/// `open_regular_file_allow_hard_links` is for a caller that either only
/// reads someone else's tree (a diagnostic source) or owns a file it never
/// reopens to write into (a migration lock, an extraction job record): it
/// accepts the exact shape `open_regular_file_refuses_a_hard_link` above
/// refuses, because a hard-link based backup routinely produces it on
/// ordinary files. The symlink-swap protection (#2404) is shared code and
/// still applies in full.
#[cfg(unix)]
#[test]
fn open_regular_file_allow_hard_links_accepts_a_hard_link_but_still_refuses_a_symlink() {
    let root = tempfile::tempdir().expect("root");
    let victim = root.path().join("victim");
    fs::write(&victim, b"content").expect("victim");
    let aliased = root.path().join("aliased");
    fs::hard_link(&victim, &aliased).expect("hard link");

    let mut options = fs::OpenOptions::new();
    options.read(true);
    open_regular_file_allow_hard_links(&aliased, "test", options)
        .expect("a hard link must be accepted by the hard-link-tolerant open");

    let outside = tempfile::tempdir().expect("outside");
    let secret = outside.path().join("secret");
    fs::write(&secret, b"do-not-read-me").expect("secret");
    let linked = root.path().join("state");
    std::os::unix::fs::symlink(&secret, &linked).expect("symlink");

    let mut options = fs::OpenOptions::new();
    options.read(true);
    let refused = open_regular_file_allow_hard_links(&linked, "test", options)
        .expect_err("a symlink must still be refused");
    assert!(
        refused
            .to_string()
            .contains("test path must be a regular file"),
        "{refused}"
    );
}

/// A Unix domain socket is refused the same way a FIFO is, and with the
/// same clean message rather than a raw, platform-specific `open` errno —
/// `open_error` falls back to a `symlink_metadata` lookup, purely to choose
/// the message, once `open` has already failed and the refusal is decided.
#[cfg(unix)]
#[test]
fn open_regular_file_refuses_a_socket_with_the_clean_message() {
    use std::os::unix::net::UnixListener;

    let root = tempfile::tempdir().expect("root");
    let path = root.path().join("state");
    let _listener = UnixListener::bind(&path).expect("bind a unix socket");

    let mut options = fs::OpenOptions::new();
    options.read(true);
    let refused = open_regular_file(&path, "test", options).expect_err("a socket");
    assert!(
        refused
            .to_string()
            .contains("test path must be a regular file"),
        "{refused}"
    );
}

/// The round-5 `open_error` fallback must not relabel a genuine regular
/// file that failed to open for an unrelated reason (permissions, here) as
/// "must be a regular file" — that message is reserved for a file that
/// really is the wrong kind. Skipped wherever permission bits aren't
/// enforced (running as root, some container/CI filesystems): checked
/// directly, by trying the same open the assertion depends on.
#[cfg(unix)]
#[test]
fn open_regular_file_keeps_the_raw_message_for_a_permission_denied_regular_file() {
    use std::os::unix::fs::PermissionsExt;

    let root = tempfile::tempdir().expect("root");
    let path = root.path().join("state");
    fs::write(&path, b"regular").expect("a real file");
    fs::set_permissions(&path, fs::Permissions::from_mode(0o000)).expect("chmod 000");

    if fs::File::open(&path).is_ok() {
        eprintln!("test: skipped, permission bits are not enforced here");
        fs::set_permissions(&path, fs::Permissions::from_mode(0o644)).expect("restore");
        return;
    }

    let mut options = fs::OpenOptions::new();
    options.read(true);
    let refused = open_regular_file(&path, "test", options).expect_err("permission denied");
    let message = refused.to_string();
    assert!(
        message.contains("cannot open test file"),
        "a permission error must keep the raw message, got: {message}"
    );
    assert!(
        !message.contains("must be a regular file"),
        "a real regular file must never be relabeled as the wrong kind, got: {message}"
    );

    // Restore permissions so the tempdir can clean itself up.
    fs::set_permissions(&path, fs::Permissions::from_mode(0o644)).expect("restore permissions");
}

#[test]
fn path_exists_tells_an_absent_path_from_a_present_one() {
    let root = tempfile::tempdir().expect("root");
    assert!(!path_exists(&root.path().join("absent")).expect("absent"));
    assert!(path_exists(root.path()).expect("present"));
}

#[cfg(unix)]
#[test]
fn path_exists_does_not_follow_a_link_and_reports_any_other_error() {
    use std::os::unix::fs::symlink;

    let root = tempfile::tempdir().expect("root");
    // A link whose target is gone still exists: it is the link that counts.
    let dangling = root.path().join("dangling");
    symlink(root.path().join("gone"), &dangling).expect("dangling link");
    assert!(path_exists(&dangling).expect("a dangling link exists"));

    // A path below a regular file is an error, not an absent path.
    let file = root.path().join("file");
    fs::write(&file, b"x").expect("file");
    let error = path_exists(&file.join("child")).expect_err("below a file");
    assert!(error.to_string().contains("cannot inspect "), "{error}");
}

#[test]
fn promote_replaces_the_target_whole_and_consumes_the_staging_file() {
    let root = tempfile::tempdir().expect("root");
    let staging = root.path().join("state.json.tmp");
    let target = root.path().join("state.json");

    fs::write(&staging, b"first").expect("staging");
    promote(&staging, &target).expect("publish over nothing");
    assert_eq!(fs::read(&target).expect("target"), b"first");
    assert!(!staging.exists());

    fs::write(&staging, b"second").expect("staging");
    promote(&staging, &target).expect("publish over a file");
    assert_eq!(fs::read(&target).expect("target"), b"second");
    assert!(!staging.exists());
}

/// On unix the barrier opens the directory, so an absent one fails. The
/// effect of the `fsync` itself is not observable from a test.
#[cfg(unix)]
#[test]
fn sync_directory_fails_on_an_absent_directory() {
    use super::sync_directory;

    let root = tempfile::tempdir().expect("root");
    sync_directory(root.path()).expect("sync a directory");
    sync_directory(&root.path().join("absent")).expect_err("an absent directory");
}