Development Documentation (main branch) - For stable release docs, see docs.rs/eidetica
Skip to main content

eidetica/instance/backend/
mod.rs

1//! The `Backend` seam: the single storage abstraction higher-level code
2//! (`Transaction`, `Store`, `Database`, `Instance`) operates through, with no
3//! branching on local vs remote.
4//!
5//! [`LocalBackend`] wraps a concrete in-process storage engine
6//! ([`BackendImpl`](crate::backend::BackendImpl)); [`RemoteBackend`] wraps a
7//! [`RemoteConnection`](crate::service::client::RemoteConnection) and
8//! translates each method to a wire RPC. The trait is the *intersection* of
9//! what both can honor with the same meaning — storage primitives that have no
10//! authorisable remote shape (secrets, verification-status mutation, raw tree
11//! dumps, scope-keyed cache) are deliberately **not** on the trait. They live
12//! on the concrete local engine, reached only where one exists via
13//! [`Backend::local_engine`].
14
15mod local;
16#[cfg(all(unix, feature = "service"))]
17mod remote;
18
19pub use local::LocalBackend;
20#[cfg(all(unix, feature = "service"))]
21pub use remote::RemoteBackend;
22
23use std::sync::Arc;
24
25use async_trait::async_trait;
26
27#[cfg(all(unix, feature = "service"))]
28use crate::service::client::RemoteConnection;
29use crate::{
30    Result,
31    backend::{BackendImpl, InstanceMetadata, VerificationStatus},
32    entry::{Entry, ID},
33    instance::WriteSource,
34    snapshot::Snapshot,
35};
36
37/// The storage operations `Transaction`/`Store`/`Database`/`Instance` perform,
38/// independent of whether storage is in-process or served by a daemon.
39///
40/// Tree-scoped methods take the tree explicitly; the remote implementation uses
41/// the argument directly (callers already pass the owning database's root), so
42/// no per-handle root needs to be bound. The only per-handle state a remote
43/// backend carries is its acting identity (see [`RemoteBackend`]).
44#[async_trait]
45pub trait Backend: Send + Sync + std::fmt::Debug {
46    /// Retrieve an entry by ID.
47    async fn get(&self, id: &ID) -> Result<Entry>;
48
49    /// Raw [`Snapshot`] of `tree` (no Verified-frontier filtering — that stays
50    /// in `Database`).
51    async fn snapshot(&self, tree: &ID) -> Result<Snapshot>;
52
53    /// Raw [`Snapshot`] of `store` within `tree`.
54    async fn store_snapshot(&self, tree: &ID, store: &str) -> Result<Snapshot>;
55
56    /// Store snapshot reachable as of a specific main-tree snapshot.
57    async fn store_snapshot_at(
58        &self,
59        tree: &ID,
60        store: &str,
61        main_snapshot: &Snapshot,
62    ) -> Result<Snapshot>;
63
64    /// Every entry of `store` reachable from `snapshot`.
65    async fn store_at(&self, tree: &ID, store: &str, snapshot: &Snapshot) -> Result<Vec<Entry>>;
66
67    /// Lowest common ancestor of `entry_ids` within `store`.
68    async fn find_merge_base(&self, tree: &ID, store: &str, entry_ids: &[ID]) -> Result<ID>;
69
70    /// Every entry on the path from `from_id` to each of `to_ids` within
71    /// `store`.
72    async fn get_path_from_to(
73        &self,
74        tree: &ID,
75        store: &str,
76        from_id: &ID,
77        to_ids: &[ID],
78    ) -> Result<Vec<ID>>;
79
80    /// Cached materialized CRDT state for `(entry_id, store)` within `tree`, if
81    /// present. `tree` keys the daemon-side cache and gates the wire RPC; the
82    /// local engine ignores it (it serves the trusted shared scope).
83    async fn get_cached_crdt_state(
84        &self,
85        tree: &ID,
86        entry_id: &ID,
87        store: &str,
88    ) -> Result<Option<Vec<u8>>>;
89
90    /// Cache materialized CRDT state for `(entry_id, store)` within `tree`.
91    async fn cache_crdt_state(
92        &self,
93        tree: &ID,
94        entry_id: &ID,
95        store: &str,
96        state: Vec<u8>,
97    ) -> Result<()>;
98
99    /// Persist an entry. Local stores it directly; remote submits it via
100    /// `DatabaseOp::SubmitSignedEntry` (stored `Unverified`, server-verified).
101    async fn put(&self, entry: Entry) -> Result<()>;
102
103    /// Durably persist a signed entry, applying `verification` locally or
104    /// submitting it over the wire. `source` informs local callback dispatch
105    /// (handled by `Instance::put_entry`) and is unused on remote.
106    async fn write_entry(
107        &self,
108        verification: VerificationStatus,
109        entry: Entry,
110        source: WriteSource,
111    ) -> Result<()>;
112
113    /// Public instance metadata (device identity, system database IDs).
114    async fn get_instance_metadata(&self) -> Result<Option<InstanceMetadata>>;
115
116    /// Persist public instance metadata.
117    async fn set_instance_metadata(&self, metadata: &InstanceMetadata) -> Result<()>;
118
119    /// The concrete in-process storage engine, if this is a local backend.
120    ///
121    /// Off-seam local-only operations (instance secrets, verification-status
122    /// mutation, `all_roots`/`get_tree` raw dumps, scope-keyed cache) are
123    /// reached through this accessor, so they are usable only where a concrete
124    /// local backend exists. Returns `None` for remote backends.
125    fn local_engine(&self) -> Option<Arc<dyn BackendImpl>> {
126        None
127    }
128
129    /// The remote connection, if this is a remote backend. Returns `None` for
130    /// local backends.
131    #[cfg(all(unix, feature = "service"))]
132    fn remote_connection(&self) -> Option<RemoteConnection> {
133        None
134    }
135}