pub struct Instance { /* private fields */ }Expand description
Database implementation on top of the storage backend.
Instance manages infrastructure only:
- Backend storage and device identity
- System databases (_users, _databases, _sync)
- User account management (create, login, list)
All database creation and key operations happen through User after login.
Instance is a cheap-to-clone handle around Arc<InstanceInternal>.
§Example
// Bootstrap a fresh instance with an initial admin user. The first user
// created on an instance is automatically granted Admin on the system
// databases.
let (instance, maybe_user) = Instance::connect_or_create(
"memory://",
NewUser::passwordless("alice"),
).await?;
let mut user = maybe_user.expect("memory:// is always fresh");
// Use User API for operations
let mut settings = Doc::new();
settings.set("name", "my_database");
let default_key = user.get_default_key()?;
let db = user.create_database(settings, &default_key).await?;Implementations§
Source§impl Instance
impl Instance
Sourcepub async fn connect(url: impl AsRef<str>) -> Result<Self>
pub async fn connect(url: impl AsRef<str>) -> Result<Self>
Open a connection to an eidetica instance described by a connection URL.
Strict load: returns InstanceError::NotInitialized when the URL
points at an embedded backend (sqlite://, postgres://, memory://)
that has no eidetica metadata yet. Use
Instance::connect_or_create to bootstrap an embedded backend on
first run.
Supported URL schemes:
sqlite://./app.db— embedded sqlite backend; URL is passed through tosqlx::sqlite, so any sqlx-accepted form works (?mode=rwc&journal_mode=WALetc.).postgres://user:pwd@host/db— embedded postgres backend; URL is passed through tosqlx::postgres.unix:///run/eidetica/sock— thin client to a running daemon.memory://— empty in-memory backend. Strict load against an empty in-memory backend always errorsNotInitialized; useconnect_or_createfor a fresh in-memory instance.memory:///path/to/snap.json— in-memory backend with a JSON snapshot file (load-on-start; snapshot writes viaInstance::flush/Instance::snapshot_to_path/ Drop fallback).
See crate::instance::url for the full URL grammar.
§Example
connect() only succeeds against an already-initialised backend.
The two-phase pattern below bootstraps once, then re-opens with
the strict load:
use eidetica::{Instance, NewUser};
let temp = tempfile::tempdir()?;
let snapshot = temp.path().join("app.json");
let url = format!("memory://{}", snapshot.display());
// First run: bootstrap and flush a snapshot to disk.
{
let (instance, maybe_user) =
Instance::connect_or_create(&url, NewUser::passwordless("alice")).await?;
let _user = maybe_user.expect("fresh bootstrap on first run");
instance.flush()?;
}
// Later: strict connect against the persisted snapshot.
let instance = Instance::connect(&url).await?;
let _user = instance.login_user("alice", None).await?;Calling connect() on a backend with no eidetica metadata returns
InstanceError::NotInitialized; reach for
Instance::connect_or_create when first-run bootstrap is part
of the expected lifecycle.
Sourcepub async fn connect_or_create(
url: impl AsRef<str>,
initial: NewUser,
) -> Result<(Self, Option<User>)>
pub async fn connect_or_create( url: impl AsRef<str>, initial: NewUser, ) -> Result<(Self, Option<User>)>
Open or initialise an eidetica instance described by a connection URL.
On the load arm: identical to Instance::connect; initial is
silently ignored and the second tuple element is None.
On the bootstrap arm: initialises the backend at the URL with the
supplied NewUser as the first admin and returns
(Instance, Some(User)). Only embedded backends
(sqlite/postgres/memory) ever take the bootstrap arm — unix:// URLs
degrade to connect (the daemon owns its own initialisation), so the
returned Option<User> is always None for unix://.
§Example
let (instance, maybe_user) = Instance::connect_or_create(
"memory://",
NewUser::passwordless("alice"),
).await?;
let mut user = match maybe_user {
Some(u) => u,
None => instance.login_user("alice", None).await?,
};Sourcepub async fn connect_or_create_backend(
backend: Box<dyn BackendImpl>,
initial: NewUser,
) -> Result<(Self, Option<User>)>
pub async fn connect_or_create_backend( backend: Box<dyn BackendImpl>, initial: NewUser, ) -> Result<(Self, Option<User>)>
Escape hatch: open or initialise an eidetica instance against a
pre-built BackendImpl (sqlite, postgres, in-memory, or custom).
Same load-or-bootstrap semantics as Instance::connect_or_create
but skips URL parsing. Useful for tests, embedded apps that want to
configure the backend’s pool/runtime manually, or backends not yet
exposed via a URL scheme.
Sourcepub async fn open_backend(backend: Box<dyn BackendImpl>) -> Result<Self>
pub async fn open_backend(backend: Box<dyn BackendImpl>) -> Result<Self>
Strict-load escape hatch: open an eidetica instance against a
pre-built BackendImpl that’s already been initialised. Mirrors
Instance::connect’s strict semantics for the URL-less case.
Errors with InstanceError::NotInitialized if the backend has no
instance metadata; use Instance::connect_or_create_backend when you want
to bootstrap on an empty backend.
Sourcepub async fn open_backend_with_clock(
backend: Box<dyn BackendImpl>,
clock: Arc<dyn Clock>,
) -> Result<Self>
pub async fn open_backend_with_clock( backend: Box<dyn BackendImpl>, clock: Arc<dyn Clock>, ) -> Result<Self>
Test variant of Instance::open_backend with an injectable clock.
Sourcepub async fn create_backend(
backend: Box<dyn BackendImpl>,
initial: NewUser,
) -> Result<(Self, User)>
pub async fn create_backend( backend: Box<dyn BackendImpl>, initial: NewUser, ) -> Result<(Self, User)>
Strict-create escape hatch: initialise an eidetica instance on a
fresh pre-built BackendImpl and bootstrap an initial admin user.
Errors with InstanceError::InstanceAlreadyExists if the backend
is already initialised; use Instance::connect_or_create_backend when the
caller doesn’t want to choose between load and create up front.
Sourcepub async fn create_backend_with_clock(
backend: Box<dyn BackendImpl>,
clock: Arc<dyn Clock>,
initial: NewUser,
) -> Result<(Self, User)>
pub async fn create_backend_with_clock( backend: Box<dyn BackendImpl>, clock: Arc<dyn Clock>, initial: NewUser, ) -> Result<(Self, User)>
Test variant of Instance::create_backend with an injectable clock.
Arg order: backend, clock, initial — clock goes in the middle so
migrating from the prior create_with_clock is a pure rename.
Sourcepub fn flush(&self) -> Result<()>
pub fn flush(&self) -> Result<()>
Flush deferred persistence state to disk.
For an Instance constructed via a memory:///path.json URL, this
writes the current backend state to the snapshot path (atomic on
POSIX — <path>.tmp then rename). For sqlite/postgres/unix
backends this is a no-op; those storage layers handle persistence
inline.
Idempotent and reentrant — call it as often as you like at
well-defined checkpoints. The snapshot path stays armed, so the
Drop fallback continues to fire on the last handle as a safety
net. The Instance (and any clones) remain fully usable after
flush() returns; this is not a shutdown.
If flush() fails (e.g. nonexistent parent directory), the error
surfaces in the Result. Drop will later try the same write and
fail the same way, logging via tracing::error!. The duplicate
signal is intentional — Drop must report what it sees.
Blocking I/O note: the snapshot write is synchronous
(std::fs::write + rename) and runs inline on the caller. Hence
the sync signature — there is no .await inside. If you’re calling
from a tokio task, this briefly blocks the runtime worker;
negligible for small snapshots.
Sourcepub fn snapshot_to_path(&self, path: impl AsRef<Path>) -> Result<()>
pub fn snapshot_to_path(&self, path: impl AsRef<Path>) -> Result<()>
Write a JSON snapshot of the in-memory backend to path.
The write goes to <path>.tmp and then renames into place. On POSIX
the rename is atomic; on Windows it is not atomic when the
destination already exists. Returns
InstanceError::SnapshotNotSupported on any backend other than
the in-memory backend.
Sourcepub fn remote_connection(&self) -> Option<RemoteConnection>
pub fn remote_connection(&self) -> Option<RemoteConnection>
Sourcepub async fn has_database(&self, root_id: &ID) -> bool
pub async fn has_database(&self, root_id: &ID) -> bool
Check if a database is present locally.
This differs from has_entry in that it checks for the active tracking
of the database by the Instance. This method checks if we’re tracking
the database’s tip state.
Sourcepub async fn login_user(
&self,
user_id: &str,
password: Option<&str>,
) -> Result<User>
pub async fn login_user( &self, user_id: &str, password: Option<&str>, ) -> Result<User>
Login a user with flexible password handling.
Returns a User session object that provides access to user operations. For password-protected users, provide the password. For passwordless users, pass None.
§Arguments
user_id- User identifier (username)password- Optional password. None for passwordless users.
§Returns
A Result containing the User session
Sourcepub fn signing_key(&self) -> Result<&PrivateKey>
pub fn signing_key(&self) -> Result<&PrivateKey>
Test-only: Get the device signing key.
This is exposed for testing purposes only. In production, use the User API.
Returns an error if this is a remote Instance that does not have access to the device key.
Sourcepub async fn enable_sync(&self) -> Result<()>
pub async fn enable_sync(&self) -> Result<()>
Initializes the Sync module for this instance.
Enables synchronization operations for this instance. This method is idempotent; calling it multiple times has no effect.
§Errors
Returns an error if the sync settings database cannot be created or if device key generation/storage fails.
Sourcepub fn sync(&self) -> Option<Arc<Sync>>
pub fn sync(&self) -> Option<Arc<Sync>>
Get a reference to the Sync module.
Returns a cheap-to-clone Arc handle to the Sync module. The Sync module uses interior mutability (AtomicBool and OnceLock) so &self methods are sufficient.
§Returns
An Option containing an Arc<Sync> if the Sync module is initialized.
Sourcepub async fn flush_sync(&self) -> Result<()>
pub async fn flush_sync(&self) -> Result<()>
Flush all pending sync operations.
This is a convenience method that processes all queued entries and retries any failed sends. If sync is not enabled, returns Ok(()).
This is useful to force pending syncs to complete, e.g. on program shutdown.
§Returns
Ok(()) if sync is not enabled or all operations completed successfully,
or an error if sends failed.
Sourcepub async fn put_entry(
&self,
tree_id: &ID,
verification: VerificationStatus,
entry: Entry,
source: WriteSource,
) -> Result<()>
pub async fn put_entry( &self, tree_id: &ID, verification: VerificationStatus, entry: Entry, source: WriteSource, ) -> Result<()>
Write an entry to the backend and dispatch callbacks.
This is the central coordination point for all entry writes in the system. All writes must go through this method to ensure:
- Entries are persisted to the backend
- Appropriate callbacks are triggered based on write source
- Hooks have full context (entry, database, instance)
Serialized per-tree against Self::put_remote_entries so
WriteEvent::previous_tips is consistent.
§Arguments
tree_id- The root ID of the database being written toverification- Authentication verification status of the entryentry- The entry to writesource- Whether this is a local or remote write
§Returns
A Result indicating success or failure
Sourcepub fn downgrade(&self) -> WeakInstance
pub fn downgrade(&self) -> WeakInstance
Downgrade to a weak reference.
Creates a weak reference that does not prevent the Instance from being dropped. This is useful for preventing circular reference cycles in dependent objects.
§Returns
A WeakInstance that can be upgraded back to a strong reference.
Trait Implementations§
Auto Trait Implementations§
impl Freeze for Instance
impl !RefUnwindSafe for Instance
impl Send for Instance
impl Sync for Instance
impl Unpin for Instance
impl UnsafeUnpin for Instance
impl !UnwindSafe for Instance
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
§impl<T> CompatExt for T
impl<T> CompatExt for T
§impl<T> Instrument for T
impl<T> Instrument for T
§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more