Skip to main content

PolicyStore

Struct PolicyStore 

Source
pub struct PolicyStore { /* private fields */ }
Expand description

Handle bound to a Repository + path prefix. Mirrors the agentstategraph-tasks TaskStore pattern.

Implementations§

Source§

impl PolicyStore

Source

pub fn new( repo: Arc<Repository>, prefix: impl Into<String>, agent_id: impl Into<String>, ) -> Self

Source

pub fn with_verifier(self, verifier: Arc<dyn SignatureVerifier>) -> Self

Install a signature verifier. Policies whose signature fails verification are treated as not-currently-active by Self::active and therefore ignored by evaluate / evaluate_change. Pass-through if never called.

Source

pub fn with_require_signed(self, require_signed: bool) -> Self

When true (and a verifier is registered) unsigned policies are filtered out of Self::active. Defaults to false.

No-op when no verifier is registered — unsigned policies stay live in pass-through mode.

Source

pub fn with_external_evaluators( self, registry: Arc<ExternalEvaluatorRegistry>, ) -> Self

Install an external-evaluator registry (0.7.5 §4a). Policies whose external_evaluator.kind matches a registered runner are dispatched through it inside evaluate_scoped / evaluate_change_scoped; policies with an unregistered kind are silently skipped (treated as not-matching). Pass-through when never called — every policy goes through the local evaluator.

Source

pub fn prefix(&self) -> &str

Source

pub fn agent_id(&self) -> &str

Source

pub fn propose( &self, ref_name: &str, policy: Policy, ) -> Result<String, PolicyError>

Write a proposed policy (not yet ratified). Fails if an entry already lives at the same path — supersede instead.

The path, version, proposed_by, and proposed_at fields are normalized/overridden on write: callers pass a Policy with path set to the intended logical path (slashes, with or without leading /), and the store writes version = 1, proposed_by = agent_id, proposed_at = now, ratified_by = None.

Source

pub fn ratify( &self, ref_name: &str, path: &str, ratifier: &str, reasoning: &str, ) -> Result<(), PolicyError>

Ratify an unratified proposal at path. Fails if the policy does not exist, is already ratified, or the ratifier / reasoning are empty.

Source

pub fn supersede( &self, ref_name: &str, path: &str, new_policy: Policy, ) -> Result<String, PolicyError>

Replace the active policy at path with new_policy. The old active version is moved to history/<version> and the new one is written at the active path with version = old + 1 and supersedes = "<path>@<old_version>".

The new policy is written as already ratified iff its ratified_by is Some(_) — callers that want propose-then- ratify semantics on a supersede can leave it None and call ratify separately.

Source

pub fn set_signature( &self, ref_name: &str, path: &str, signature: PolicySignature, ) -> Result<(), PolicyError>

Attach (or replace) a signature on the active policy at path.

Called by the MCP policy_sign tool (§2c of the 0.7.5 plan) after the registered PolicySigner has produced the signature bytes over the canonical form. The existing active policy is loaded, policy.signature is overwritten, and the result is written back under IntentCategory::PolicySign so the commit log stays filterable.

This is the only writer that may mutate Policy::signature post-ratification without bumping version — signatures are detached metadata over the canonical body and the canonical body excludes signature itself.

Source

pub fn get( &self, ref_name: &str, path: &str, version: Option<u64>, ) -> Result<Policy, PolicyError>

Fetch the active (ratified or proposed) policy at path, or a pinned historical version when version is Some(_).

Source

pub fn list( &self, ref_name: &str, prefix_filter: Option<&str>, ) -> Result<Vec<Policy>, PolicyError>

List every policy (active versions only) whose path starts with prefix_filter (path form without leading slash). None lists all policies under the store prefix.

Source

pub fn list_scoped( &self, ref_name: &str, prefix_filter: Option<&str>, tenant_filter: Option<&str>, ) -> Result<Vec<Policy>, PolicyError>

Tenant-scoped variant of Self::list (0.7.5 §3b).

tenant_filter semantics:

  • None → no filter; every policy (globals + all tenants) is visible. This is the back-compat path used by Self::list.
  • Some(tid) → only policies with tenant_id == Some(tid) or tenant_id == None (globals always apply) are returned.
Source

pub fn active( &self, ref_name: &str, prefix_filter: Option<&str>, ) -> Result<Vec<Policy>, PolicyError>

List currently-active policies: ratified AND active_from <= now AND not expired.

A ratified policy with a future active_from is skipped the same way an unratified proposal is — it’s scheduled but not yet live. When a signature verifier has been installed via Self::with_verifier, policies whose signature fails verification — or whose signature is missing while require_signed is true — are also filtered out with the same not-currently-active semantics. No verifier → pass through (back-compat).

Source

pub fn active_scoped( &self, ref_name: &str, prefix_filter: Option<&str>, tenant_filter: Option<&str>, ) -> Result<Vec<Policy>, PolicyError>

Tenant-scoped variant of Self::active (0.7.5 §3b).

Applies the tenant filter after the signature filter so signature-rejected policies are never surfaced regardless of the tenant. tenant_filter semantics match Self::list_scoped: None is no filter; Some(tid) keeps policies with tenant_id == Some(tid) OR tenant_id == None (globals always apply). policies_for_situation_scoped, evaluate_scoped, and evaluate_change_scoped all route through this method, so they inherit the filter for free.

Source

pub fn history( &self, ref_name: &str, path: &str, ) -> Result<Vec<Policy>, PolicyError>

Walk the supersedes chain. Returned oldest-first (version 1 → current).

Source

pub fn policies_for_situation( &self, ref_name: &str, situation: &Situation, ) -> Result<Vec<Policy>, PolicyError>

Every ratified policy whose situation_selector matches situation.

Source

pub fn policies_for_situation_scoped( &self, ref_name: &str, situation: &Situation, tenant_filter: Option<&str>, ) -> Result<Vec<Policy>, PolicyError>

Tenant-scoped variant of Self::policies_for_situation (0.7.5 §3b). Delegates to Self::active_scoped so the tenant filter is inherited.

Source

pub fn evaluate( &self, ref_name: &str, situation: &Situation, action: &str, agent_id: &str, ) -> Result<Decision, PolicyError>

Authorization evaluation — POLICY_V1.md §5.

Source

pub fn evaluate_scoped( &self, ref_name: &str, situation: &Situation, action: &str, agent_id: &str, tenant_filter: Option<&str>, ) -> Result<Decision, PolicyError>

Tenant-scoped variant of Self::evaluate (0.7.5 §3b). Routes through Self::policies_for_situation_scoped so only policies with tenant_id == Some(tid) or tenant_id == None contribute to the decision.

External-evaluator dispatch (0.7.5 §4a): matched policies carrying an external_evaluator whose kind is registered in the store’s registry are routed to the external runner; their decisions are combined with the local evaluator’s verdict under the existing deny > require_approval > allow precedence. Policies whose external kind is not registered are skipped.

Source

pub fn evaluate_change( &self, ref_name: &str, proposal: &ChangeProposal, ) -> Result<Decision, PolicyError>

Change-proposal evaluation — POLICY_V1.md §22.2.

Source

pub fn evaluate_change_scoped( &self, ref_name: &str, proposal: &ChangeProposal, tenant_filter: Option<&str>, ) -> Result<Decision, PolicyError>

Tenant-scoped variant of Self::evaluate_change (0.7.5 §3b). Routes through Self::active_scoped.

External-evaluator dispatch (0.7.5 §4a): matched policies carrying an external_evaluator whose kind is registered in the store’s registry are routed to the external runner for their portion of the decision; their decisions are combined with the local change-evaluator’s verdict under deny > require_approval > allow precedence. Policies whose external kind is not registered are skipped. External runners see proposal.action / proposal.agent_id and an empty Situation — the change-proposal flow does not carry a situation context of its own.

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> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

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

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

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, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

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

fn try_from(value: U) -> Result<T, !>

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.
Source§

impl<T> WithSubscriber for T

Source§

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

fn with_current_subscriber(self) -> WithDispatch<Self> ⓘ

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