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}