pub struct AclEntry {Show 15 fields
pub did: String,
pub role: Role,
pub label: Option<String>,
pub allowed_contexts: Vec<String>,
pub created_at: u64,
pub created_by: String,
pub expires_at: Option<u64>,
pub kind: ConsumerKind,
pub capabilities: Vec<Capability>,
pub device: Option<DeviceBinding>,
pub version: u32,
pub step_up_approver: Option<String>,
pub step_up_require: Option<StepUpMode>,
pub approve_scope: ApproveScope,
pub allowed_keys: Option<BTreeSet<String>>,
}Expand description
An entry in the Access Control List.
Fields§
§did: String§role: Role§label: Option<String>§allowed_contexts: Vec<String>§created_at: u64§created_by: String§expires_at: Option<u64>Unix-epoch seconds at which this entry expires and should be pruned by
the background sweeper. None is permanent (existing pre-Phase-2
behavior; entries serialized before this field existed deserialize with
this default).
kind: ConsumerKindConsumer kind: Companion (user-driven) vs Service (headless). New in
M1 (vault-credential-manager design). #[serde(default)] ⇒ pre-M1
rows deserialise as Service { Daemon }.
capabilities: Vec<Capability>Fine-grained capability set. Empty Vec on legacy rows; the auth
layer falls back to derived_capabilities_for_role when this
is empty so existing behaviour stays byte-identical.
device: Option<DeviceBinding>Optional Companion/Service device-binding metadata. Populated by
the M4 device/register/0.1 flow; absent on legacy rows and on
pure ACL entries that don’t represent a registered device.
version: u32Optimistic-concurrency version. Incremented on every
successful update; the route layer’s If-Match header
compares against this and returns 409 Conflict on a
stale write. Closes M6 from the May 2026 security review
— two admins editing the same DID concurrently no longer
silently lose one update.
#[serde(default)] so pre-versioning rows deserialise
with version=0. The first update bumps it to 1.
step_up_approver: Option<String>VID authorized to ratify an AAL2 step-up for this subject — the
recipient the VTA addresses an auth/step-up/approve-request/0.1 to
when a gated operation resolves to delegated mode (the holder’s
mobile authenticator or browser companion). None means no delegated
approver is configured: under a delegated floor the operation
fail-closes (the subject can’t self-approve a delegated requirement).
Mirrors the spec’s AclEntry.stepUp.approver.
#[serde(default)] so pre-existing rows deserialise as None.
step_up_require: Option<StepUpMode>Per-entry step-up override raising the system floor for this subject —
the spec’s AclEntry.stepUp.require. ADDITIVE-ONLY: the effective mode
is the strictest of (system floor, this override), so an override weaker
than the floor is ignored (see [StepUpMode::strictest]). Restricted to
self / delegated (a per-subject override never relaxes to
delegated-any); the ACL op layer rejects other values.
#[serde(default)] so pre-existing rows deserialise as None (no
override; the system floor applies unchanged).
approve_scope: ApproveScopeAuthority to confer access via an approval, decoupled from the
authority to act. Read only by the two conferral paths
(compute_delegated_contexts, delegated_any_approver_covers); it
never feeds require_admin/has_context_access. Lets an approver be
least-privilege (act nowhere, authorize across contexts). #[serde(default)]
⇒ pre-existing rows deserialise as ApproveScope::None.
allowed_keys: Option<BTreeSet<String>>Key ids this entry may invoke the signing oracle on (#818). None =
every key in allowed_contexts (today’s behaviour; pre-existing rows
deserialise here). Some(∅) = authorized on no keys — the
narrowest grant, never a wildcard. Intersects with the context scope;
it can only narrow, never widen, so an entry naming a key outside its
contexts still cannot use it. Read through AclEntry::key_scope,
never by testing emptiness — see KeyScope. Mirrors the canonical
acl/_shared/0.1/acl-entry#allowedKeys.
Implementations§
Source§impl AclEntry
impl AclEntry
Sourcepub fn new(
did: impl Into<String>,
role: Role,
created_by: impl Into<String>,
) -> Self
pub fn new( did: impl Into<String>, role: Role, created_by: impl Into<String>, ) -> Self
Create an entry with the required identity fields. Optional metadata
takes sensible defaults: no label, no allowed_contexts, never
expires, default ConsumerKind, no capabilities, no device
binding, version = 0, and created_at = now. Layer non-defaults on
with the with_* builder methods.
This is the single construction entry point — adding a new optional field here defaults it everywhere, so callers don’t churn.
Sourcepub fn with_created_at(self, created_at: u64) -> Self
pub fn with_created_at(self, created_at: u64) -> Self
Override created_at (defaults to now). Use when replaying a known
timestamp — bootstrap import, tests, migration.
Sourcepub fn with_label(self, label: Option<String>) -> Self
pub fn with_label(self, label: Option<String>) -> Self
Set the optional human-readable label.
Sourcepub fn with_contexts(self, allowed_contexts: Vec<String>) -> Self
pub fn with_contexts(self, allowed_contexts: Vec<String>) -> Self
Set the allowed-contexts (scope) list.
Sourcepub fn with_expires_at(self, expires_at: Option<u64>) -> Self
pub fn with_expires_at(self, expires_at: Option<u64>) -> Self
Set the optional expiry (unix seconds). None is permanent.
Sourcepub fn with_kind(self, kind: ConsumerKind) -> Self
pub fn with_kind(self, kind: ConsumerKind) -> Self
Set the consumer kind (Companion vs Service).
Sourcepub fn with_capabilities(self, capabilities: Vec<Capability>) -> Self
pub fn with_capabilities(self, capabilities: Vec<Capability>) -> Self
Set the fine-grained capability set.
Sourcepub fn with_device(self, device: Option<DeviceBinding>) -> Self
pub fn with_device(self, device: Option<DeviceBinding>) -> Self
Attach optional Companion/Service device-binding metadata.
Sourcepub fn with_step_up_approver(self, approver: Option<String>) -> Self
pub fn with_step_up_approver(self, approver: Option<String>) -> Self
Set the delegated step-up approver VID (stepUp.approver).
Sourcepub fn with_step_up_require(self, require: Option<StepUpMode>) -> Self
pub fn with_step_up_require(self, require: Option<StepUpMode>) -> Self
Set the per-entry step-up override (stepUp.require).
Sourcepub fn with_approve_scope(self, approve_scope: ApproveScope) -> Self
pub fn with_approve_scope(self, approve_scope: ApproveScope) -> Self
Set the approve-authority scope (what this DID may confer via approval, without any authority to act).
Sourcepub fn with_allowed_keys(self, allowed_keys: Option<BTreeSet<String>>) -> Self
pub fn with_allowed_keys(self, allowed_keys: Option<BTreeSet<String>>) -> Self
Set the signing-oracle key filter. None = no filter (every key in
the entry’s contexts); Some(∅) = no keys at all.
Sourcepub fn key_scope(&self) -> KeyScope
pub fn key_scope(&self) -> KeyScope
This entry’s authority over signing-oracle key ids, decoded from
allowed_keys. Use this rather than reading the Option directly —
see KeyScope for why (Some(∅) and None are opposite grants,
and a bare emptiness test collapses them).
Sourcepub fn act_scope(&self) -> ActScope
pub fn act_scope(&self) -> ActScope
This entry’s authority to act, decoded from (role, allowed_contexts). Use this rather than reading allowed_contexts
directly — see ActScope for why.
Sourcepub fn can_act_in(&self, context_id: &str) -> bool
pub fn can_act_in(&self, context_id: &str) -> bool
Whether this entry may act in context_id, honouring context ancestry.
Sourcepub fn is_super_admin(&self) -> bool
pub fn is_super_admin(&self) -> bool
Whether this entry is a super-admin: an admin whose ActScope is
unrestricted, mirroring AuthClaims::is_super_admin.
Sourcepub fn with_version(self, version: u32) -> Self
pub fn with_version(self, version: u32) -> Self
Set the optimistic-concurrency version (defaults to 0).
Sourcepub fn is_expired(&self, now_unix: u64) -> bool
pub fn is_expired(&self, now_unix: u64) -> bool
Returns true if this entry has passed its configured expires_at.
Permanent entries (no expires_at) never expire.
Sourcepub fn etag(&self) -> String
pub fn etag(&self) -> String
Strong validator string suitable for the ETag response
header and the If-Match precondition on subsequent
updates. Combines the DID and the version so a moving
version increment can never accidentally validate against
the wrong row.
Format: W/"<did_hash>:<version>" — W/ because the
underlying ACL entry isn’t byte-identical between writes
(timestamps, label edits don’t change semantic content
but do change bytes); the did_hash is a 64-bit FxHash
to keep the header short.