Skip to main content

ruvix_vecgraph/
vector_store.rs

1//! Kernel-resident vector store implementation.
2//!
3//! Vector stores are kernel objects containing HNSW-indexed vectors.
4//! All mutations are proof-gated via the `vector_put_proved` syscall.
5//!
6//! # Design (from ADR-087 Section 4.3)
7//!
8//! ```rust,ignore
9//! pub struct KernelVectorStore {
10//!     hnsw_region: RegionHandle,       // slab region for HNSW graph nodes
11//!     data_region: RegionHandle,       // slab region for vector data
12//!     witness_region: RegionHandle,    // append-only mutation witness log
13//!     coherence_config: CoherenceConfig,
14//!     proof_policy: ProofPolicy,
15//!     dimensions: u32,
16//!     capacity: u32,
17//! }
18//! ```
19
20use crate::coherence::{CoherenceConfig, CoherenceTracker};
21use crate::hnsw::{HnswConfig, HnswRegion};
22use crate::proof_policy::{ProofPolicy, ProofVerifier};
23use crate::witness::WitnessLog;
24use crate::Result;
25
26use ruvix_region::backing::MemoryBacking;
27use ruvix_region::slab::{SlabAllocator, SlotHandle};
28use ruvix_types::{
29    CapRights, Capability, CoherenceMeta, KernelError, ProofAttestation, ProofToken, RegionHandle,
30    VectorKey, VectorStoreHandle,
31};
32
33/// Size of vector entry header (key + coherence meta).
34const VECTOR_ENTRY_HEADER_SIZE: usize = 8 + core::mem::size_of::<CoherenceMeta>();
35
36/// A vector entry stored in the data region.
37#[derive(Debug, Clone)]
38pub struct VectorEntry {
39    /// The vector key.
40    pub key: VectorKey,
41
42    /// Coherence metadata.
43    pub coherence: CoherenceMeta,
44
45    /// The vector data (f32 values as bytes).
46    pub data: Vec<f32>,
47}
48
49impl VectorEntry {
50    /// Creates a new vector entry.
51    #[inline]
52    #[must_use]
53    pub fn new(key: VectorKey, data: Vec<f32>, coherence: CoherenceMeta) -> Self {
54        Self {
55            key,
56            coherence,
57            data,
58        }
59    }
60}
61
62/// Builder for creating kernel vector stores.
63pub struct VectorStoreBuilder {
64    dimensions: u32,
65    capacity: u32,
66    hnsw_config: HnswConfig,
67    coherence_config: CoherenceConfig,
68    proof_policy: ProofPolicy,
69}
70
71impl VectorStoreBuilder {
72    /// Creates a new vector store builder.
73    #[inline]
74    #[must_use]
75    pub fn new(dimensions: u32, capacity: u32) -> Self {
76        Self {
77            dimensions,
78            capacity,
79            hnsw_config: HnswConfig::default(),
80            coherence_config: CoherenceConfig::default(),
81            proof_policy: ProofPolicy::standard(),
82        }
83    }
84
85    /// Sets the HNSW configuration.
86    #[inline]
87    #[must_use]
88    pub fn with_hnsw_config(mut self, config: HnswConfig) -> Self {
89        self.hnsw_config = config;
90        self
91    }
92
93    /// Sets the coherence configuration.
94    #[inline]
95    #[must_use]
96    pub fn with_coherence_config(mut self, config: CoherenceConfig) -> Self {
97        self.coherence_config = config;
98        self
99    }
100
101    /// Sets the proof policy.
102    #[inline]
103    #[must_use]
104    pub fn with_proof_policy(mut self, policy: ProofPolicy) -> Self {
105        self.proof_policy = policy;
106        self
107    }
108
109    /// Builds the kernel vector store.
110    ///
111    /// # Type Parameters
112    ///
113    /// * `B` - The memory backing type
114    ///
115    /// # Arguments
116    ///
117    /// * `data_backing` - Memory backing for vector data slab
118    /// * `hnsw_backing` - Memory backing for HNSW node slab
119    /// * `witness_backing` - Memory backing for witness log
120    /// * `data_handle` - Region handle for data region
121    /// * `hnsw_handle` - Region handle for HNSW region
122    /// * `witness_handle` - Region handle for witness region
123    /// * `store_id` - Unique store identifier
124    pub fn build<B: MemoryBacking>(
125        self,
126        data_backing: B,
127        hnsw_backing: B,
128        witness_backing: B,
129        data_handle: RegionHandle,
130        hnsw_handle: RegionHandle,
131        witness_handle: RegionHandle,
132        store_id: u32,
133    ) -> Result<KernelVectorStore<B>> {
134        KernelVectorStore::new(
135            data_backing,
136            hnsw_backing,
137            witness_backing,
138            data_handle,
139            hnsw_handle,
140            witness_handle,
141            self.dimensions,
142            self.capacity,
143            self.hnsw_config,
144            self.coherence_config,
145            self.proof_policy,
146            store_id,
147        )
148    }
149}
150
151/// Key-to-slot mapping for vector lookup.
152struct KeyMap {
153    /// Simple array-based mapping (for small stores).
154    /// In production, use a hash map or B-tree in a slab region.
155    entries: [(VectorKey, SlotHandle); 256],
156    count: usize,
157}
158
159impl KeyMap {
160    fn new() -> Self {
161        Self {
162            entries: [(VectorKey::new(0), SlotHandle::invalid()); 256],
163            count: 0,
164        }
165    }
166
167    fn get(&self, key: VectorKey) -> Option<SlotHandle> {
168        for i in 0..self.count {
169            if self.entries[i].0 == key {
170                return Some(self.entries[i].1);
171            }
172        }
173        None
174    }
175
176    fn insert(&mut self, key: VectorKey, handle: SlotHandle) -> Result<()> {
177        // Check for existing key
178        for i in 0..self.count {
179            if self.entries[i].0 == key {
180                self.entries[i].1 = handle;
181                return Ok(());
182            }
183        }
184
185        // Add new entry
186        if self.count >= 256 {
187            return Err(KernelError::LimitExceeded);
188        }
189
190        self.entries[self.count] = (key, handle);
191        self.count += 1;
192        Ok(())
193    }
194
195    fn remove(&mut self, key: VectorKey) -> Option<SlotHandle> {
196        for i in 0..self.count {
197            if self.entries[i].0 == key {
198                let handle = self.entries[i].1;
199                // Swap with last
200                self.entries[i] = self.entries[self.count - 1];
201                self.entries[self.count - 1] = (VectorKey::new(0), SlotHandle::invalid());
202                self.count -= 1;
203                return Some(handle);
204            }
205        }
206        None
207    }
208
209    fn len(&self) -> usize {
210        self.count
211    }
212}
213
214/// Kernel-resident vector store.
215///
216/// Implements the `vector_get` and `vector_put_proved` syscall interfaces.
217pub struct KernelVectorStore<B: MemoryBacking> {
218    /// Slab region for vector data.
219    data_slab: SlabAllocator<B>,
220
221    /// HNSW index region.
222    hnsw_region: HnswRegion<B>,
223
224    /// Append-only witness log.
225    witness_log: WitnessLog<B>,
226
227    /// Region handles for capability checking.
228    data_handle: RegionHandle,
229    hnsw_handle: RegionHandle,
230
231    /// Key to slot mapping.
232    key_map: KeyMap,
233
234    /// Coherence tracker.
235    coherence_tracker: CoherenceTracker,
236
237    /// Proof verifier.
238    proof_verifier: ProofVerifier,
239
240    /// Vector dimensions.
241    dimensions: u32,
242
243    /// Maximum capacity.
244    capacity: u32,
245
246    /// Store identifier.
247    store_id: u32,
248
249    /// Store handle.
250    handle: VectorStoreHandle,
251}
252
253impl<B: MemoryBacking> KernelVectorStore<B> {
254    /// Creates a new kernel vector store.
255    #[allow(clippy::too_many_arguments)]
256    pub fn new(
257        data_backing: B,
258        hnsw_backing: B,
259        witness_backing: B,
260        data_handle: RegionHandle,
261        hnsw_handle: RegionHandle,
262        witness_handle: RegionHandle,
263        dimensions: u32,
264        capacity: u32,
265        hnsw_config: HnswConfig,
266        coherence_config: CoherenceConfig,
267        proof_policy: ProofPolicy,
268        store_id: u32,
269    ) -> Result<Self> {
270        // Calculate slot size for vector data
271        // Header (key + coherence) + vector data (dimensions * 4 bytes)
272        let slot_size = VECTOR_ENTRY_HEADER_SIZE + (dimensions as usize) * 4;
273
274        let data_slab = SlabAllocator::new(data_backing, slot_size, capacity as usize)?;
275        let hnsw_region = HnswRegion::new(hnsw_backing, hnsw_config, capacity as usize)?;
276        let witness_log = WitnessLog::new(
277            witness_backing,
278            capacity as usize * 2, // Allow for some updates per vector
279            witness_handle,
280            store_id,
281        )?;
282
283        Ok(Self {
284            data_slab,
285            hnsw_region,
286            witness_log,
287            data_handle,
288            hnsw_handle,
289            key_map: KeyMap::new(),
290            coherence_tracker: CoherenceTracker::new(coherence_config),
291            proof_verifier: ProofVerifier::new(proof_policy),
292            dimensions,
293            capacity,
294            store_id,
295            handle: VectorStoreHandle::new(store_id, 0),
296        })
297    }
298
299    /// Returns the store handle.
300    #[inline]
301    #[must_use]
302    pub const fn handle(&self) -> VectorStoreHandle {
303        self.handle
304    }
305
306    /// Returns the number of vectors stored.
307    #[inline]
308    #[must_use]
309    pub fn len(&self) -> usize {
310        self.key_map.len()
311    }
312
313    /// Returns true if the store is empty.
314    #[inline]
315    #[must_use]
316    pub fn is_empty(&self) -> bool {
317        self.key_map.len() == 0
318    }
319
320    /// Returns the capacity.
321    #[inline]
322    #[must_use]
323    pub const fn capacity(&self) -> u32 {
324        self.capacity
325    }
326
327    /// Returns the dimensions.
328    #[inline]
329    #[must_use]
330    pub const fn dimensions(&self) -> u32 {
331        self.dimensions
332    }
333
334    /// Implements the `vector_get` syscall.
335    ///
336    /// Returns the vector data and its coherence metadata.
337    /// Requires READ right on the capability.
338    ///
339    /// # Arguments
340    ///
341    /// * `key` - The vector key
342    /// * `capability` - Capability authorizing the read
343    ///
344    /// # Returns
345    ///
346    /// The vector data and coherence metadata.
347    pub fn vector_get(
348        &self,
349        key: VectorKey,
350        capability: &Capability,
351    ) -> Result<(Vec<f32>, CoherenceMeta)> {
352        // Check READ right
353        if !capability.has_rights(CapRights::READ) {
354            return Err(KernelError::InsufficientRights);
355        }
356
357        // Look up the key
358        let slot_handle = self.key_map.get(key).ok_or(KernelError::NotFound)?;
359
360        // Read the slot
361        let slot_size = VECTOR_ENTRY_HEADER_SIZE + (self.dimensions as usize) * 4;
362        let mut buf = vec![0u8; slot_size];
363        self.data_slab.read(slot_handle, &mut buf)?;
364
365        // Parse header
366        let key_bytes: [u8; 8] = buf[0..8].try_into().map_err(|_| KernelError::InternalError)?;
367        let stored_key = VectorKey::new(u64::from_le_bytes(key_bytes));
368
369        if stored_key != key {
370            return Err(KernelError::InternalError);
371        }
372
373        // Parse coherence metadata
374        let coherence_bytes = &buf[8..8 + core::mem::size_of::<CoherenceMeta>()];
375        let coherence = parse_coherence_meta(coherence_bytes)?;
376
377        // Parse vector data
378        let data_start = VECTOR_ENTRY_HEADER_SIZE;
379        let mut data = Vec::with_capacity(self.dimensions as usize);
380        for i in 0..self.dimensions as usize {
381            let offset = data_start + i * 4;
382            let bytes: [u8; 4] = buf[offset..offset + 4]
383                .try_into()
384                .map_err(|_| KernelError::InternalError)?;
385            data.push(f32::from_le_bytes(bytes));
386        }
387
388        Ok((data, coherence))
389    }
390
391    /// Implements the `vector_put_proved` syscall.
392    ///
393    /// Writes a vector with proof verification.
394    /// Requires PROVE right on the capability.
395    ///
396    /// # Arguments
397    ///
398    /// * `key` - The vector key
399    /// * `data` - The vector data
400    /// * `proof` - The proof token authorizing the mutation
401    /// * `capability` - Capability authorizing the write
402    /// * `current_time_ns` - Current time for proof verification
403    ///
404    /// # Returns
405    ///
406    /// A proof attestation on success.
407    pub fn vector_put_proved(
408        &mut self,
409        key: VectorKey,
410        data: &[f32],
411        proof: &ProofToken,
412        capability: &Capability,
413        current_time_ns: u64,
414    ) -> Result<ProofAttestation> {
415        // Validate dimensions
416        if data.len() != self.dimensions as usize {
417            return Err(KernelError::InvalidArgument);
418        }
419
420        // Compute mutation hash
421        let mutation_hash = compute_vector_mutation_hash(key, data);
422
423        // Verify proof
424        let attestation =
425            self.proof_verifier
426                .verify(proof, &mutation_hash, current_time_ns, capability)?;
427
428        // Check if this is an insert or update
429        let is_update = self.key_map.get(key).is_some();
430
431        if is_update {
432            // Update existing vector
433            let slot_handle = self.key_map.get(key).unwrap();
434
435            // Read old metadata for coherence tracking
436            let slot_size = VECTOR_ENTRY_HEADER_SIZE + (self.dimensions as usize) * 4;
437            let mut buf = vec![0u8; slot_size];
438            self.data_slab.read(slot_handle, &mut buf)?;
439
440            let old_coherence_bytes = &buf[8..8 + core::mem::size_of::<CoherenceMeta>()];
441            let old_coherence = parse_coherence_meta(old_coherence_bytes)?;
442
443            // Create new coherence metadata
444            let new_coherence = self.coherence_tracker.on_entry_mutated(
445                &old_coherence,
446                self.coherence_tracker.config().initial_coherence,
447                attestation.proof_term_hash,
448            );
449
450            // Write updated data
451            self.write_vector_slot(slot_handle, key, data, &new_coherence)?;
452        } else {
453            // Insert new vector
454            let slot_handle = self.data_slab.alloc()?;
455
456            // Create coherence metadata
457            let coherence = self
458                .coherence_tracker
459                .create_initial_meta(attestation.proof_term_hash);
460
461            // Write data
462            self.write_vector_slot(slot_handle, key, data, &coherence)?;
463
464            // Update key map
465            self.key_map.insert(key, slot_handle)?;
466
467            // Allocate HNSW node
468            let _hnsw_handle = self.hnsw_region.alloc_node(0, slot_handle)?;
469
470            // Track coherence
471            self.coherence_tracker
472                .on_entry_added(coherence.coherence_score);
473        }
474
475        // Record in witness log
476        self.witness_log
477            .record_vector_mutation(key, attestation, current_time_ns)?;
478
479        Ok(attestation)
480    }
481
482    /// Writes vector data to a slot.
483    fn write_vector_slot(
484        &mut self,
485        slot_handle: SlotHandle,
486        key: VectorKey,
487        data: &[f32],
488        coherence: &CoherenceMeta,
489    ) -> Result<()> {
490        let slot_size = VECTOR_ENTRY_HEADER_SIZE + (self.dimensions as usize) * 4;
491        let mut buf = vec![0u8; slot_size];
492
493        // Write key
494        buf[0..8].copy_from_slice(&key.raw().to_le_bytes());
495
496        // Write coherence metadata
497        serialize_coherence_meta(coherence, &mut buf[8..8 + core::mem::size_of::<CoherenceMeta>()]);
498
499        // Write vector data
500        let data_start = VECTOR_ENTRY_HEADER_SIZE;
501        for (i, &value) in data.iter().enumerate() {
502            let offset = data_start + i * 4;
503            buf[offset..offset + 4].copy_from_slice(&value.to_le_bytes());
504        }
505
506        self.data_slab.write(slot_handle, &buf)?;
507        Ok(())
508    }
509
510    /// Returns the proof policy.
511    #[inline]
512    #[must_use]
513    pub const fn proof_policy(&self) -> &ProofPolicy {
514        self.proof_verifier.policy()
515    }
516
517    /// Returns the coherence tracker.
518    #[inline]
519    #[must_use]
520    pub const fn coherence_tracker(&self) -> &CoherenceTracker {
521        &self.coherence_tracker
522    }
523
524    /// Returns witness log statistics.
525    #[inline]
526    #[must_use]
527    pub fn witness_entry_count(&self) -> usize {
528        self.witness_log.entry_count()
529    }
530
531    /// Returns the witness log fill ratio.
532    #[inline]
533    #[must_use]
534    pub fn witness_fill_ratio(&self) -> f32 {
535        self.witness_log.fill_ratio()
536    }
537}
538
539/// Computes a hash of a vector mutation for proof verification.
540fn compute_vector_mutation_hash(key: VectorKey, data: &[f32]) -> [u8; 32] {
541    // Simple hash for demonstration
542    // In production, use SHA-256 or similar
543    let mut hash = [0u8; 32];
544
545    // Include key
546    let key_bytes = key.raw().to_le_bytes();
547    hash[0..8].copy_from_slice(&key_bytes);
548
549    // Include data hash (simple XOR for demonstration)
550    for (i, &value) in data.iter().enumerate() {
551        let bytes = value.to_le_bytes();
552        let offset = (8 + (i * 4)) % 24; // Stay within remaining space
553        for j in 0..4 {
554            hash[offset + j] ^= bytes[j];
555        }
556    }
557
558    hash
559}
560
561/// Parses coherence metadata from bytes.
562fn parse_coherence_meta(bytes: &[u8]) -> Result<CoherenceMeta> {
563    if bytes.len() < core::mem::size_of::<CoherenceMeta>() {
564        return Err(KernelError::BufferTooSmall);
565    }
566
567    let coherence_score = u16::from_le_bytes([bytes[0], bytes[1]]);
568    let mutation_epoch = u64::from_le_bytes(bytes[2..10].try_into().unwrap());
569
570    let mut proof_attestation_hash = [0u8; 32];
571    proof_attestation_hash.copy_from_slice(&bytes[10..42]);
572
573    let last_access_ns = u64::from_le_bytes(bytes[42..50].try_into().unwrap());
574    let access_count = u32::from_le_bytes(bytes[50..54].try_into().unwrap());
575
576    Ok(CoherenceMeta {
577        coherence_score,
578        mutation_epoch,
579        proof_attestation_hash,
580        last_access_ns,
581        access_count,
582    })
583}
584
585/// Serializes coherence metadata to bytes.
586fn serialize_coherence_meta(meta: &CoherenceMeta, buf: &mut [u8]) {
587    buf[0..2].copy_from_slice(&meta.coherence_score.to_le_bytes());
588    buf[2..10].copy_from_slice(&meta.mutation_epoch.to_le_bytes());
589    buf[10..42].copy_from_slice(&meta.proof_attestation_hash);
590    buf[42..50].copy_from_slice(&meta.last_access_ns.to_le_bytes());
591    buf[50..54].copy_from_slice(&meta.access_count.to_le_bytes());
592}
593
594#[cfg(test)]
595mod tests {
596    use super::*;
597    use ruvix_region::backing::StaticBacking;
598    use ruvix_types::{ObjectType, ProofPayload, ProofTier};
599
600    fn create_test_capability() -> Capability {
601        Capability::new(
602            1,
603            ObjectType::VectorStore,
604            CapRights::READ | CapRights::WRITE | CapRights::PROVE,
605            0,
606            1,
607        )
608    }
609
610    fn create_test_proof(data: &[f32], key: VectorKey, valid_until_ns: u64, nonce: u64) -> ProofToken {
611        let mutation_hash = compute_vector_mutation_hash(key, data);
612        ProofToken::new(
613            mutation_hash,
614            ProofTier::Standard,
615            ProofPayload::Hash { hash: mutation_hash },
616            valid_until_ns,
617            nonce,
618        )
619    }
620
621    #[test]
622    fn test_vector_store_creation() {
623        let data_backing = StaticBacking::<16384>::new();
624        let hnsw_backing = StaticBacking::<16384>::new();
625        let witness_backing = StaticBacking::<16384>::new();
626
627        let store = VectorStoreBuilder::new(4, 10) // Small dimensions and capacity for tests
628            .build(
629                data_backing,
630                hnsw_backing,
631                witness_backing,
632                RegionHandle::new(1, 0),
633                RegionHandle::new(2, 0),
634                RegionHandle::new(3, 0),
635                1,
636            )
637            .unwrap();
638
639        assert_eq!(store.dimensions(), 4);
640        assert_eq!(store.capacity(), 10);
641        assert!(store.is_empty());
642    }
643
644    #[test]
645    fn test_vector_put_and_get() {
646        let data_backing = StaticBacking::<16384>::new();
647        let hnsw_backing = StaticBacking::<16384>::new();
648        let witness_backing = StaticBacking::<16384>::new();
649
650        let mut store = VectorStoreBuilder::new(4, 10)
651            .with_proof_policy(ProofPolicy::reflex())
652            .build(
653                data_backing,
654                hnsw_backing,
655                witness_backing,
656                RegionHandle::new(1, 0),
657                RegionHandle::new(2, 0),
658                RegionHandle::new(3, 0),
659                1,
660            )
661            .unwrap();
662
663        let cap = create_test_capability();
664        let key = VectorKey::new(42);
665        let data = vec![1.0, 2.0, 3.0, 4.0];
666        let proof = create_test_proof(&data, key, 1_000_000_000, 1);
667
668        // Put vector
669        let attestation = store
670            .vector_put_proved(key, &data, &proof, &cap, 500_000_000)
671            .unwrap();
672
673        assert_eq!(store.len(), 1);
674        assert!(attestation.verification_timestamp_ns > 0);
675
676        // Get vector
677        let (retrieved_data, coherence) = store.vector_get(key, &cap).unwrap();
678
679        assert_eq!(retrieved_data, data);
680        assert_eq!(coherence.coherence_score, 10000); // Initial coherence
681    }
682
683    #[test]
684    fn test_vector_put_proof_rejected() {
685        let data_backing = StaticBacking::<16384>::new();
686        let hnsw_backing = StaticBacking::<16384>::new();
687        let witness_backing = StaticBacking::<16384>::new();
688
689        let mut store = VectorStoreBuilder::new(4, 10)
690            .build(
691                data_backing,
692                hnsw_backing,
693                witness_backing,
694                RegionHandle::new(1, 0),
695                RegionHandle::new(2, 0),
696                RegionHandle::new(3, 0),
697                1,
698            )
699            .unwrap();
700
701        let cap = create_test_capability();
702        let key = VectorKey::new(42);
703        let data = vec![1.0, 2.0, 3.0, 4.0];
704
705        // Create proof with wrong hash
706        let wrong_proof = ProofToken::new(
707            [0u8; 32], // Wrong hash
708            ProofTier::Standard,
709            ProofPayload::Hash { hash: [0u8; 32] },
710            1_000_000_000,
711            1,
712        );
713
714        let result = store.vector_put_proved(key, &data, &wrong_proof, &cap, 500_000_000);
715
716        assert_eq!(result, Err(KernelError::ProofRejected));
717        assert!(store.is_empty()); // No change
718    }
719
720    #[test]
721    fn test_vector_put_expired_proof() {
722        let data_backing = StaticBacking::<16384>::new();
723        let hnsw_backing = StaticBacking::<16384>::new();
724        let witness_backing = StaticBacking::<16384>::new();
725
726        let mut store = VectorStoreBuilder::new(4, 10)
727            .build(
728                data_backing,
729                hnsw_backing,
730                witness_backing,
731                RegionHandle::new(1, 0),
732                RegionHandle::new(2, 0),
733                RegionHandle::new(3, 0),
734                1,
735            )
736            .unwrap();
737
738        let cap = create_test_capability();
739        let key = VectorKey::new(42);
740        let data = vec![1.0, 2.0, 3.0, 4.0];
741
742        // Create expired proof
743        let expired_proof = create_test_proof(&data, key, 100_000_000, 1);
744
745        // Current time is after expiry
746        let result = store.vector_put_proved(key, &data, &expired_proof, &cap, 500_000_000);
747
748        assert_eq!(result, Err(KernelError::ProofRejected));
749    }
750
751    #[test]
752    fn test_vector_put_nonce_reuse() {
753        let data_backing = StaticBacking::<16384>::new();
754        let hnsw_backing = StaticBacking::<16384>::new();
755        let witness_backing = StaticBacking::<16384>::new();
756
757        let mut store = VectorStoreBuilder::new(4, 10)
758            .with_proof_policy(ProofPolicy::reflex())
759            .build(
760                data_backing,
761                hnsw_backing,
762                witness_backing,
763                RegionHandle::new(1, 0),
764                RegionHandle::new(2, 0),
765                RegionHandle::new(3, 0),
766                1,
767            )
768            .unwrap();
769
770        let cap = create_test_capability();
771        let key1 = VectorKey::new(1);
772        let key2 = VectorKey::new(2);
773        let data1 = vec![1.0, 2.0, 3.0, 4.0];
774        let data2 = vec![5.0, 6.0, 7.0, 8.0];
775
776        // First put succeeds
777        let proof1 = create_test_proof(&data1, key1, 1_000_000_000, 42);
778        store
779            .vector_put_proved(key1, &data1, &proof1, &cap, 500_000_000)
780            .unwrap();
781
782        // Second put with same nonce fails
783        let proof2 = create_test_proof(&data2, key2, 1_000_000_000, 42);
784        let result = store.vector_put_proved(key2, &data2, &proof2, &cap, 500_000_001);
785
786        assert_eq!(result, Err(KernelError::ProofRejected));
787    }
788
789    #[test]
790    fn test_vector_get_not_found() {
791        let data_backing = StaticBacking::<16384>::new();
792        let hnsw_backing = StaticBacking::<16384>::new();
793        let witness_backing = StaticBacking::<16384>::new();
794
795        let store = VectorStoreBuilder::new(4, 10)
796            .build(
797                data_backing,
798                hnsw_backing,
799                witness_backing,
800                RegionHandle::new(1, 0),
801                RegionHandle::new(2, 0),
802                RegionHandle::new(3, 0),
803                1,
804            )
805            .unwrap();
806
807        let cap = create_test_capability();
808        let result = store.vector_get(VectorKey::new(999), &cap);
809
810        assert_eq!(result, Err(KernelError::NotFound));
811    }
812
813    #[test]
814    fn test_vector_get_insufficient_rights() {
815        let data_backing = StaticBacking::<16384>::new();
816        let hnsw_backing = StaticBacking::<16384>::new();
817        let witness_backing = StaticBacking::<16384>::new();
818
819        let store = VectorStoreBuilder::new(4, 10)
820            .build(
821                data_backing,
822                hnsw_backing,
823                witness_backing,
824                RegionHandle::new(1, 0),
825                RegionHandle::new(2, 0),
826                RegionHandle::new(3, 0),
827                1,
828            )
829            .unwrap();
830
831        // Capability without READ right
832        let cap = Capability::new(1, ObjectType::VectorStore, CapRights::WRITE, 0, 1);
833
834        let result = store.vector_get(VectorKey::new(1), &cap);
835
836        assert_eq!(result, Err(KernelError::InsufficientRights));
837    }
838
839    #[test]
840    fn test_witness_log_recording() {
841        let data_backing = StaticBacking::<16384>::new();
842        let hnsw_backing = StaticBacking::<16384>::new();
843        let witness_backing = StaticBacking::<16384>::new();
844
845        let mut store = VectorStoreBuilder::new(4, 10)
846            .with_proof_policy(ProofPolicy::reflex())
847            .build(
848                data_backing,
849                hnsw_backing,
850                witness_backing,
851                RegionHandle::new(1, 0),
852                RegionHandle::new(2, 0),
853                RegionHandle::new(3, 0),
854                1,
855            )
856            .unwrap();
857
858        let cap = create_test_capability();
859
860        // Perform multiple mutations
861        for i in 0..5 {
862            let key = VectorKey::new(i);
863            let data = vec![i as f32; 4];
864            let proof = create_test_proof(&data, key, 1_000_000_000, i);
865            store
866                .vector_put_proved(key, &data, &proof, &cap, 500_000_000)
867                .unwrap();
868        }
869
870        assert_eq!(store.witness_entry_count(), 5);
871    }
872}