Skip to main content

ScopeReport

Struct ScopeReport 

Source
pub struct ScopeReport {
Show 29 fields pub declared_containment: ContainmentClass, pub achieved_containment: ContainmentClass, pub floor_met: bool, pub shortfall_reason: Option<String>, pub enforcement_tier: &'static str, pub userns_grant: Option<UsernsGrant>, pub filesystem_scope: Option<String>, pub workdir_exec: bool, pub read_only_paths: Vec<String>, pub read_only_advisory_for: Vec<String>, pub preopens: Vec<PreopenReport>, pub network_allow: Vec<String>, pub unix_sockets: bool, pub shell_allow: Vec<String>, pub spawn_allow: Vec<String>, pub env_allow: Vec<String>, pub plan_submit: bool, pub install_skill: Vec<String>, pub install_tool: Vec<String>, pub interpreter_runtime_grants: Vec<String>, pub exports_files: Option<ExportsFilesReport>, pub peer_files: Option<PeerFilesReport>, pub peer_fetch_allow: Vec<String>, pub state_stores: Vec<StateStoreReport>, pub configured_artifacts: Vec<String>, pub staged_runtime_grants: Vec<String>, pub runtime_writes: Vec<RuntimeWriteReport>, pub io_max: IoMaxReport, pub filesystem_boundary: FilesystemBoundaryReport,
}
Expand description

Read-only answer to “what would this capsule actually be allowed to do on this host?”.

Built from the already-parsed CapabilityPolicy plus one host tier probe. Nothing here stages artifacts, touches a registry, creates a workdir or resolves DNS — it is a diagnostic, so it reports even when Self::floor_met is false.

Fields§

§declared_containment: ContainmentClass

Strongest class any source asked for (manifest / workspace config / --containment).

§achieved_containment: ContainmentClass

Strongest class this host can actually back, derived from the tier probe alone.

§floor_met: bool

achieved_containment >= declared_containment. false means mur run would refuse.

§shortfall_reason: Option<String>

The missing mechanism when floor_met is false; absent otherwise.

§enforcement_tier: &'static str

The probed host tier, as a stable wire name.

§userns_grant: Option<UsernsGrant>

Where this host’s permission to create an unprivileged user namespace comes from, as UsernsGrant::wire_name, or null off Linux, where AppArmor does not exist and the question has no answer.

Always serialized, on the same terms as Self::workdir_exec: the key’s absence identifies a runtime that predates it, not a host that was not asked. Reported because three very different hosts — no AppArmor, hardening switched off host-wide, and the shipped profile confining mur — otherwise produce byte-identical reports while differing enormously in what else on the machine can create a user namespace.

§filesystem_scope: Option<String>

capabilities.filesystem.scope, verbatim.

§workdir_exec: bool

capabilities.filesystem.workdir_exec. Always present (never skipped when false), so a consumer can tell “declared false” from “this runtime predates the key” by the field’s presence rather than by its value.

true is the only manifest declaration that lowers Self::achieved_containment, which is why it is reported next to the grants rather than buried: reading achieved: advisory on a Landlock-capable host makes sense only alongside it.

§read_only_paths: Vec<String>

capabilities.filesystem.read_only, verbatim — the workdir subtrees the capsule may read but must not write. Empty when the capsule declares none, which is the whole workdir writable. Always serialized as an array (never skipped), on the same terms as Self::state_stores: an absent key identifies a runtime that predates the field, not a capsule that declared nothing.

The one field here that subtracts rather than grants, which is why it is reported beside Self::read_only_advisory_for rather than alone: a protection stated without its qualification reads as a boundary the capsule does not have.

§read_only_advisory_for: Vec<String>

The entries of Self::shell_allow that Self::read_only_paths is only advisory against — the same set W-SEC-017 fires for, resolved by the same read_only_advisory_for both this report and mur doctor call.

Empty means the declaration is enforced for every readable call. Non-empty names the binaries whose own file I/O the dispatch-time write-intent analyser cannot read, so the report cannot claim more than W-SEC-017 already qualifies. Always serialized as an array (never skipped), on the same terms as Self::read_only_paths, and always empty when that list is: nothing is advisory against a protection that was never declared.

§preopens: Vec<PreopenReport>

The filesystem surface every guest-bearing artifact in this capsule is preopened into, one entry per runtime: tool, runtime: driver and runtime: hook entry, in manifest order. Empty when the capsule declares only skills. Always serialized as an array (never skipped), on the same terms as Self::state_stores: an absent key identifies a runtime that predates the field, not a capsule with no guest artifacts.

This is the field that answers “what can this tool read and write?”. Self::filesystem_scope above does not: it is the capsule-wide declaration, and it is joined into no preopen root.

Copied through untouched, exactly as the reports beside it are — resolving a preopen changes no class, no floor and no tier.

§network_allow: Vec<String>

capabilities.network.allow, verbatim — declared destinations, not resolved IPs.

§unix_sockets: bool

capabilities.network.unix_sockets.

§shell_allow: Vec<String>

capabilities.shell.allow, verbatim.

§spawn_allow: Vec<String>

capabilities.spawn.allow, verbatim.

§env_allow: Vec<String>

capabilities.env.allow, verbatim.

§plan_submit: bool

capabilities.plan.submit — whether the model is offered the submit-plan tool. Always serialized (never skipped when false), on the same terms as Self::workdir_exec: an absent key identifies a runtime that predates the grant, not a capsule that declined it.

It opens no host resource of its own. It is reported beside the grants because it changes how much of every other grant one model turn can reach: a plan runs many steps against this capsule’s own tools, shell allowlist and spawn grant without a turn in between.

§install_skill: Vec<String>

capabilities.install.skill, verbatim: which skill artifacts manage.pull may install into the session. Always serialized, an empty list included, on Self::plan_submit’s terms: an absent key identifies a runtime that predates the grant.

It opens no host resource of its own. A pulled skill adds text to what the model reads and gains no grant.

§install_tool: Vec<String>

capabilities.install.tool, verbatim: which tool artifacts, WASM or native, manage.pull may install. Always serialized, on Self::install_skill’s terms. A pulled tool runs on this capsule’s own grants and gains none of its own.

§interpreter_runtime_grants: Vec<String>

Every host directory a capabilities.shell.interpreter_runtime grant opens, rendered as <binary>: <dir>[ (list_dir)]. These are the paths outside the workdir that stay reachable even at scoped.

§exports_files: Option<ExportsFilesReport>

The read-only file surface exports.files declares, or null when the capsule declares none. Always serialized (never skipped), so a consumer can tell “this runtime predates exports” from “this capsule declined to export anything” by the field’s presence rather than by its value — the same terms Self::workdir_exec is reported on.

An export is a disclosure, not a grant: it never appears in Self::achieved_containment, and declaring one cannot change any other field of this report. It is printed beside the grants because it is the other direction of the same question — what crosses the capsule boundary, and which way.

§peer_files: Option<PeerFilesReport>

The peer-handoff surface exports.peer_files declares, or null when the capsule declares none. Always serialized, on the same terms as Self::exports_files.

A separate authoriser from exports.files, over a separate subtree: a capsule may declare either, both or neither, and this field says nothing about the other. Like every export it is a disclosure rather than a grant, and declaring it cannot change Self::achieved_containment, Self::enforcement_tier or Self::floor_met.

§peer_fetch_allow: Vec<String>

capabilities.peer_fetch.allow, verbatim — the peers this capsule may redeem a handle against. Empty when the block is absent, which is deny.

A separate list from Self::network_allow and never an alias for it: a destination reachable by send is not redeemable unless it also appears here, because ingesting a peer’s bytes into this workdir is a different question from being allowed to talk to it.

§state_stores: Vec<StateStoreReport>

Every durable state store this session’s artifacts declare, one entry per declaring artifact, empty when none does. Always serialized as an array (never skipped), on the same terms as Self::exports_files: an absent key identifies a runtime that predates state stores, not a capsule that declined one.

Declaring a store cannot change Self::achieved_containment, Self::floor_met or Self::enforcement_tier — it is copied through untouched, exactly as the export reports beside it are. It is reported because it is the one grant that opens a path outside every workdir, which nothing else in this block would reveal.

§configured_artifacts: Vec<String>

Every artifact whose manifest entry declares a config: block, in manifest order, empty when none does. Always serialized as an array (never skipped), on the same terms as Self::state_stores: an absent key identifies a runtime that predates artifact config, not a capsule that declined it.

Names only. What an artifact is configured with is operator-authored plaintext that belongs in the manifest and in nothing this report — or trace.jsonl, which carries this report verbatim — writes out.

Declaring config cannot change Self::achieved_containment, Self::floor_met or Self::enforcement_tier: it adds one environment variable to one guest and reaches no grant. Reported because a variable an artifact silently depends on is otherwise invisible to an operator reading the capsule’s scope.

§staged_runtime_grants: Vec<String>

Every capabilities.shell.staged_runtime grant, rendered as <binary>: <source_path> (pin: <pin>). These are host runtime trees a sealed capsule asks to have bind-mounted read-only into its composed root.

Reported whatever the floor and whatever this host can back — a capsule that declares a staged runtime and cannot run here is exactly the case an operator is inspecting when they reach for --explain-scope, so hiding the grant behind a met floor would blank out the diagnostic in the one case it is for.

§runtime_writes: Vec<RuntimeWriteReport>

Every path the runtime itself writes inside the capsule’s workdir, so a consumer for whom that workdir is the deliverable can subtract them and be left with what the capsule changed. Always serialized as an array (never skipped), on the same terms as Self::state_stores: an absent key identifies a runtime that predates the declaration, not a runtime that writes nothing.

The one field here that describes what the runtime does rather than what the capsule may do. It is reported beside the grants because it answers the other half of “what is this workdir”, and because a consumer that has to maintain its own list of runtime entries silently falls behind every time the set changes — enabling sealed being exactly that case.

Keyed on the tier this session would install: Self::enforcement_tier capped by Self::declared_containment, which is what decides whether a composed root is built at all. Reading enforcement_tier: mountns+pivot_root+landlock+seccomp next to a report with no sealed rows is that cap, not a disagreement — the host could seal and the capsule did not ask. See crate::workdir_writes.

§io_max: IoMaxReport

The declared capabilities.resources.cgroup_io_bytes_per_sec ceiling, and whether it is actually on this session’s cgroup scope.

Always serialized (never skipped), on the same terms as Self::workdir_exec and Self::state_stores: an absent key identifies a runtime that predates the field, not a host that was not asked. IoMaxStatus::NotProbed is what “not asked” looks like, and it is a value in the report rather than a missing key.

The one field here filled from a performed operation rather than from the policy: at launch it carries the outcome of the real io.max write, established before session_start is written so trace.jsonl cannot claim a ceiling the kernel refused; under --explain-scope it carries the outcome of the same write against a throwaway cgroup that is created and removed. Declaring a ceiling that does not apply changes no class and refuses no launch — io.max is the one non-fatal cgroup limit — so this field is the only place the shortfall appears.

§filesystem_boundary: FilesystemBoundaryReport

What this session’s filesystem mechanism does not restrict — the block --explain-scope prints as Not protected here, directly under Containment.

Always serialized (never skipped), on the same terms as Self::state_stores: an absent key identifies a runtime that predates the field, and an empty FilesystemBoundaryReport::not_protected identifies a session with nothing to disclaim.

Derived in [filesystem_boundary] from [crate::sandbox::applied_tier] — the same value Self::runtime_writes is keyed on — and from no CapabilityPolicy field. Not from Self::achieved_containment, which reports the class this host can back: a capsule declaring less than sealed on a sealed-capable host reads achieved: sealed and composes no root, so a boundary keyed on the class would report an absence the capsule does not have. It stands beside the grants rather than among them because it qualifies every one at once.

Implementations§

Source§

impl ScopeReport

Source

pub fn render(&self) -> String

Human-readable rendering used by mur run --explain-scope without --json.

Trait Implementations§

Source§

impl Clone for ScopeReport

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

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

Performs copy-assignment from source. Read more
Source§

impl Debug for ScopeReport

Source§

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

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

impl Eq for ScopeReport

Source§

impl PartialEq for ScopeReport

Source§

fn eq(&self, other: &Self) -> bool

Equality operator ==. Read more
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Inequality operator !=. Read more
Source§

impl Serialize for ScopeReport

Source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where __S: Serializer,

Serialize this value into the given Serde serializer. Read more
Source§

impl StructuralPartialEq for ScopeReport

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

impl<Q, K> Equivalent<K> for Q
where Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,

Source§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
Source§

impl<Q, K> Equivalent<K> for Q
where Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,

Source§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<Q, K> Equivalent<K> for Q
where Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,

Source§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. 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> 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
Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

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

Initializes a with the given initializer. Read more
Source§

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

Dereferences the given pointer. Read more
Source§

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

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. 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 = !

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