pub struct RegistryService<M, S, A, C, I>{ /* private fields */ }Expand description
Composed registry orchestration.
Generic over the five infrastructure traits so tests can substitute fast,
deterministic backends; production binaries wire the SQLCipher /
OsKeyring / SystemClock / UuidV4IdGenerator quartet.
See evault-store-memory/tests/registry_service.rs for a full example
exercising the public API with the in-memory backends.
Implementations§
Source§impl<M, S, A> RegistryService<M, S, A, SystemClock, UuidV4IdGenerator>
impl<M, S, A> RegistryService<M, S, A, SystemClock, UuidV4IdGenerator>
Sourcepub const fn with_defaults(metadata: M, secrets: S, audit: A) -> Self
pub const fn with_defaults(metadata: M, secrets: S, audit: A) -> Self
Construct a registry with the default real-time clock and v4 UUID generator.
Convenience wrapper around Self::new for production wiring.
Source§impl<M, S, A, C, I> RegistryService<M, S, A, C, I>
impl<M, S, A, C, I> RegistryService<M, S, A, C, I>
Sourcepub const fn new(metadata: M, secrets: S, audit: A, clock: C, id_gen: I) -> Self
pub const fn new(metadata: M, secrets: S, audit: A, clock: C, id_gen: I) -> Self
Construct a registry from its trait dependencies.
Sourcepub fn create_var(
&self,
name: &str,
group: Group,
kind: VarKind,
value: SecretString,
) -> Result<VarId, CoreError>
pub fn create_var( &self, name: &str, group: Group, kind: VarKind, value: SecretString, ) -> Result<VarId, CoreError>
Create a new variable, route its value to the correct storage tier, and emit an audit entry.
The name is validated, then uniqueness is checked. If both succeed,
the variable’s id is generated through the injected
IdGenerator and its timestamps through the injected Clock.
§Atomicity (v1)
The implementation performs a best-effort compensation on
value-tier failure: if the metadata row was written and the value
write then fails, the metadata row is rolled back so the name is
not permanently reserved. A failure of the audit append after a
successful create is not compensated — the variable exists,
only the audit row is missing — and surfaces as
CoreError::Metadata. Full cross-tier atomic commit is on the
roadmap for the SQLCipher backend.
§Errors
Returns CoreError::Metadata for validation, uniqueness, or
storage failures; CoreError::Secret if writing to the secret
store fails. Empty value is rejected with
MetadataError::Invalid.
Sourcepub fn update_value(
&self,
id: VarId,
value: SecretString,
) -> Result<(), CoreError>
pub fn update_value( &self, id: VarId, value: SecretString, ) -> Result<(), CoreError>
Update the value of an existing variable.
Routes the new value to the same storage tier the variable was
created with. The metadata record’s length is refreshed but the
kind is preserved.
The value-tier write happens before the metadata length update so that a partial failure cannot leave metadata advertising a length that no value in storage actually has.
§Atomicity (v1)
The kind on the metadata record is trusted to route the write. If
the metadata is corrupted with a kind that disagrees with the
actual stored value, this method will route to the corrupted
kind’s tier and may leak the prior value to the opposite tier.
Subsequent get_value calls then surface
CoreError::TierMismatch. Hardening this path (probe-and-clear
the opposite tier defensively) is scheduled for the v1.1 SQLCipher
landing.
A failure of the audit append after a successful value write is
reported as an error but the value is persisted; same v1 limitation
as documented on Self::create_var.
§Errors
Returns MetadataError::VarNotFound if the variable does not
exist, MetadataError::Invalid if the value is empty, or any
error from the underlying storage tier.
Sourcepub fn get_value(&self, id: VarId) -> Result<Option<SecretString>, CoreError>
pub fn get_value(&self, id: VarId) -> Result<Option<SecretString>, CoreError>
Retrieve the value of a variable regardless of storage tier.
If the metadata record claims one VarKind but the value is
found in the opposite tier, this method returns
CoreError::TierMismatch rather than silently treating the
situation as “value missing”. This surfaces corruption that
bypassed the service layer’s normal routing.
§Errors
Returns any propagated storage error.
Returns CoreError::TierMismatch when the variable’s metadata
kind disagrees with where the value actually lives (corruption
detection). Returns Ok(None) if the variable does not exist or
has no value in either tier.
Sourcepub fn delete_var(&self, id: VarId) -> Result<(), CoreError>
pub fn delete_var(&self, id: VarId) -> Result<(), CoreError>
Delete a variable and every value stored for it across all tiers. Idempotent: deleting an absent variable is a successful no-op.
Order of operations: metadata first (cascading plain values + links),
then a defensive secrets.delete for both kinds. The metadata-first
order guarantees that a transient secret-tier failure cannot leave a
“zombie” variable that the user can see but never read; the
defensive secret delete on Plain kind closes a corruption-recovery
path. A secret-tier failure after metadata has been deleted is
returned to the caller but the variable is, from any reader’s
perspective, gone.
§Errors
Propagates storage failures.
Sourcepub fn create_project(
&self,
name: impl Into<String>,
path: PathBuf,
) -> Result<ProjectId, CoreError>
pub fn create_project( &self, name: impl Into<String>, path: PathBuf, ) -> Result<ProjectId, CoreError>
Sourcepub fn list_projects(&self) -> Result<Vec<Project>, CoreError>
pub fn list_projects(&self) -> Result<Vec<Project>, CoreError>
List every project, sorted by the backend’s contract (usually by name for deterministic output).
§Errors
Propagates storage failures.
Sourcepub fn link_var(
&self,
project_id: ProjectId,
var_id: VarId,
profile: Profile,
alias: Option<String>,
) -> Result<(), CoreError>
pub fn link_var( &self, project_id: ProjectId, var_id: VarId, profile: Profile, alias: Option<String>, ) -> Result<(), CoreError>
Link a variable to a project under the given profile, optionally renaming it for the project’s context.
§Errors
Returns MetadataError::ProjectNotFound or
MetadataError::VarNotFound if either side is missing.
Sourcepub fn unlink_var(
&self,
project_id: ProjectId,
var_id: VarId,
profile: &Profile,
) -> Result<(), CoreError>
pub fn unlink_var( &self, project_id: ProjectId, var_id: VarId, profile: &Profile, ) -> Result<(), CoreError>
Remove a linkage. Idempotent: removing an absent link is Ok(()).
Emits an AuditAction::Unlinked entry only when a linkage was
actually removed. A call on an absent triple is a successful
no-op and is not audited (avoids audit-log pollution that would
degrade forensic value).
§Atomicity (v1)
A failure of the audit append after a successful linkage removal
is surfaced as an error but the linkage is gone. Callers should
not retry, since a retry of unlink_var would return Ok(())
(the triple is now absent). Same v1 limitation as
Self::create_var; a dedicated AuditWriteFailed variant is
scheduled for v1.1.
§Errors
Propagates storage failures.