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

Instance

Struct Instance 

Source
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

Source

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 to sqlx::sqlite, so any sqlx-accepted form works (?mode=rwc&journal_mode=WAL etc.).
  • postgres://user:pwd@host/db — embedded postgres backend; URL is passed through to sqlx::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 errors NotInitialized; use connect_or_create for a fresh in-memory instance.
  • memory:///path/to/snap.json — in-memory backend with a JSON snapshot file (load-on-start; snapshot writes via Instance::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.

Source

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?,
};
Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

pub fn backend(&self) -> &Arc<dyn Backend>

Get a reference to the backend seam.

Source

pub fn remote_connection(&self) -> Option<RemoteConnection>

The remote connection backing this instance, if it was created via connect. Returns None for local instances.

Useful for constructing a Database that routes reads through the Database-level wire API while sharing the same connection and session as the instance’s write path.

Source

pub async fn has_entry(&self, id: &ID) -> bool

Check if an entry exists in storage.

Source

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.

Source

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

Source

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.

Source

pub fn id(&self) -> PublicKey

Get the instance identity (public key).

§Returns

The instance’s public key identity.

Source

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.

Source

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.

Source

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.

Source

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 to
  • verification - Authentication verification status of the entry
  • entry - The entry to write
  • source - Whether this is a local or remote write
§Returns

A Result indicating success or failure

Source

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§

Source§

impl Clone for Instance

Source§

fn clone(&self) -> Instance

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Instance

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Handle for Instance

§

fn handle(&self) -> Self

Creates a new handle to the same underlying resource. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<T> CompatExt for T

§

fn compat(self) -> Compat<T>

Applies the [Compat] adapter by value. Read more
§

fn compat_ref(&self) -> Compat<&T>

Applies the [Compat] adapter by shared reference. Read more
§

fn compat_mut(&mut self) -> Compat<&mut T>

Applies the [Compat] adapter by mutable reference. Read more
Source§

impl<T> DynClone for T
where T: Clone,

Source§

fn __clone_box(&self, _: Private) -> *mut ()

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<T> FromRef<T> for T
where T: Clone,

§

fn from_ref(input: &T) -> T

Converts to this type from a reference to the input type.
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts 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 more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts 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
§

impl<T> Pointable for T

§

const ALIGN: usize

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
§

impl<T> PolicyExt for T
where T: ?Sized,

§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns [Action::Follow] only if self and other return Action::Follow. Read more
§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns [Action::Follow] if either self or other returns Action::Follow. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a [WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a [WithDispatch] wrapper. Read more