pub struct ExecutionGate;Expand description
The spec §7.1 execution gate: a pure, reusable authorization component evaluated immediately before tool implementation lookup/execution.
Order of checks (each fails typed, closed, without acquiring anything):
- resolve wire name → exact live
ToolId(deterministic reverse mapping; aliases cannot pick a different identity); - the identity must be cataloged;
- the identity must be core (pinned digest intact) or hold an exact
session activation grant covering (session, tool, digest) — see
[
grant_covers_execution]: catalog generation drift alone does NOT deny at execution time; only a real change to the called tool’s record (digest, presence, provenance) does; - the record’s CURRENT trust provenance must equal the provenance the
session PINNED at core-build/activation time — this closes the
coherent-imposter hole (same
ToolId, schema-identical body, different but internally consistent source/provenance); - source trust is re-evaluated conservatively — see the honesty note
on [
check_source_trust]: this re-checks typed source/provenance consistency and denies Unknown/Unverified, but does NOT yet consult live manifest permission/revocation state (Task 20 integrates the permission/revocation policy); - side-effect/confirmation policy (interim:
Unclassifiedis allowed only for verified-provenance capabilities — Task 24 adds real effect classes); - only then is the implementation acquired.
TOCTOU: callers must resolve, authorize, and acquire against ONE
consistent registry borrow/lock guard — ExecutionGate::authorize_wire_call
does exactly that for a &ToolRegistry held under the caller’s read lock.
Implementations§
Source§impl ExecutionGate
impl ExecutionGate
Sourcepub fn resolve(
registry: &ToolRegistry,
wire_name: &str,
) -> Result<ResolvedToolCall, ToolAuthorizationError>
pub fn resolve( registry: &ToolRegistry, wire_name: &str, ) -> Result<ResolvedToolCall, ToolAuthorizationError>
Resolve an incoming API/wire name against one registry snapshot.
Authorize a resolved identity against one catalog snapshot and one
session tool set. On success — and only then — the implementation is
acquired from the catalog record’s factory and returned inside the
typed AuthorizedToolCall. Failure acquires nothing and leaves the
session set untouched (the gate never mutates it).
SECURITY INVARIANT (per-tool digest + provenance validation): no
tool executes unless its CURRENT catalog record — presence of the
exact ToolId, schema digest, and source/trust provenance — exactly
matches what the session pinned at core-build/activation time.
Catalog generation inequality ALONE is no longer a denial reason at
execution time: an unrelated background mutation (e.g. a plugin
load mid-round) must not kill in-flight calls to schema-identical
tools. Any REAL drift of the called tool’s record — changed digest,
removal, changed provenance (even to an internally coherent one) —
still denies typed and closed.
ACCEPTED RESIDUAL RISK: SchemaDigest hashes the schema JSON
only, not the implementation factory. A record replaced in place
with an identical schema AND identical pinned provenance but a
different implementation passes this gate; the exposure is bounded
by the deterministic round-top rebuild (runtime/stream.rs), which
re-pins the set against the current catalog between provider rounds.
Resolve + authorize + acquire against ONE registry borrow (one consistent snapshot under the caller’s read lock — no TOCTOU between resolution and implementation acquisition).
Auto Trait Implementations§
impl Freeze for ExecutionGate
impl RefUnwindSafe for ExecutionGate
impl Send for ExecutionGate
impl Sync for ExecutionGate
impl Unpin for ExecutionGate
impl UnsafeUnpin for ExecutionGate
impl UnwindSafe for ExecutionGate
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> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can
then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.Source§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be
further downcast into Rc<ConcreteType> where ConcreteType implements Trait.Source§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.Source§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.