Chio/Docs
LOGIN · JOIN

Blast radius

Find the exact set of capabilities a finding reaches, freeze new issuance first, and contain only from a complete view.

Concept chio@5f63e6e

On this page

Containment reaches exactly the capabilities a finding's lineage reaches. A capability that shares the tenant, or happens to sit near the incident, stays outside the set.

Chio resolves that set from a bounded walk of recorded causal lineage and certifies the walk complete before any plan can target it. A plan that suspends a capability set or freezes issuance may only touch the capabilities an exact result names, and the set stays fixed while response authority is acquired.

Lineage resolutionChoose a resolution.
research tenantdelegatesproducedσ1repositoryσ2dependency-reviewρread receiptσ4indexerresolveapprovefreezere-resolvesuspendExact: repository, dependency-reviewresearch tenantdelegatesproducedσ1repositoryσ2dependency-reviewρread receiptσ4indexerresolveapprovefreezere-resolvesuspendExact: repository, dependency-review

The walk certifies two capabilities

The bounded walk from the finding's seed reaches dependency-review and its read receipt. The result narrows to capability nodes, so the affected set is repository and dependency-review; the receipt stays causal history and the indexer, with no path from the seed, stays outside. Revocation.

An illustrative research-tenant lineage (the repository seed, its dependency-review child and read receipt, and an unrelated indexer) with the steps from resolution to suspension. Query bounds, commit metadata, and the other seventeen incomplete reasons are left out.

The causal graph decides who is affected

A shared tenant is too broad an answer, since unrelated tasks belong to the same organization. A list of running workers is too narrow, since a delegated child can keep its authority after its parent process stops. The causal authority graph records the relationship a response is meant to follow.

CausalBlastRadiusResolver resolves a BlastRadiusRequest by walking a CausalLineageSnapshot for the finding's tenant, supplied by its CausalLineageStore port, and retains the request's bounds and commit index. The walk stays within the request's own bounds, and the supported ceilings are depth 64, 4,096 nodes, and 8,192 edges; a request past them returns Incomplete with InvalidQueryBounds. Choose bounds that can certify the scope you intend.

The result narrows to capability nodes. In the figure, the read receipt belongs to the causal history the walk commits to, and only the capability nodes enter the suspension set. The indexer has no path from the seed, so it stays outside. The resolver is generic over its lineage and fence stores and binds neither to a storage backend.

A walk that cannot certify completeness returns no set

An Exact result certifies the complete capability set: sorted reachable IDs, the affected-set hash, the graph-slice hash, and the snapshot metadata. An Incomplete result carries a reason and its metadata, and no affected set.

crates/security/chio-security-types/src/ports_parts/part_03.rs209–220rust
pub enum BlastRadiusResult {
    Exact {
        metadata: BlastRadiusSnapshotMetadata,
        sorted_affected_ids: RecordIdSet,
        affected_set_hash: Digest32,
        graph_slice_hash: Digest32,
    },
    Incomplete {
        metadata: BlastRadiusSnapshotMetadata,
        reason: BlastRadiusIncompleteReason,
    },
}

The private resolve_snapshot returns a BlastRadiusIncompleteReason the moment it cannot vouch for the snapshot. A snapshot that reports its own depth, node, or edge truncation is TruncatedSnapshot; disagreeing observed and authoritative commit indexes are ReplicaLag; a completeness watermark that is missing or behind that commit is MissingCompletenessWatermark. Eighteen reasons cover the ways a walk can fail to certify a complete, single-tenant, acyclic graph, and only a walk that clears every check returns BlastRadiusResult::Exact.

Store-provided completeness must cover the requested slice, because a plausible list with one missing edge would leave authority active while the response reported success. An incomplete result sends the operator back to repair the knowledge, or to a narrower action with its own justification; after replica lag or a changed source epoch, recover authoritative, complete history before approval.

All eighteen incomplete reasons
crates/security/chio-security-types/src/ports_parts/part_03.rs177–196rust
pub enum BlastRadiusIncompleteReason {
    InvalidQueryBounds,
    LineageStoreFailure,
    CrossTenantSnapshot,
    TruncatedSnapshot,
    InvalidLineageMetadata,
    ReplicaLag,
    MissingCompletenessWatermark,
    UnreportedTruncation,
    CrossTenantNode,
    ConflictingNode,
    MissingSeed,
    CrossTenantEdge,
    CorruptEdge,
    DepthTruncated,
    UnreachableNode,
    CycleCorruption,
    AffectedSetInvalid,
    HashFailure,
}

Issuance is frozen before any capability is suspended

A plan is lineage-scoped when its effects include SuspendCapabilitySet or FreezeIssuance. ResponsePlan::validate_shape requires such a plan to begin with FreezeIssuance and refuses any other order with ResponseShapeError::MissingIssuanceFence. The kernel's active-response admission repeats the rule on its own path, refusing with KernelError::GovernedTransactionDenied.

On a kernel with an installed issuance admission authority, the freeze holds the lineage closed while the rest of the plan applies. issue_capability refuses outright, and issue_capability_with_security_context checks the lineage's freeze with the installed CapabilityIssuanceAdmissionAuthority before issuing and again before publication. The executor treats the freeze as its own checkpoint: once FreezeIssuance installs, ResponseExecutor::execute returns with the updated record before applying the remaining effects.

Plans that only throttle, restrict egress, suspend a session, or escalate an alert bind an exact session and resolve no blast radius.

Read more in Issuance governance, which covers the kernel-embedding path that enforces the freeze.

The approved set is re-resolved under the fence

acquire_validated_fence is the only path in Chio's resolver that turns an approved BlastRadiusResult into a live lineage fence. It checks the exact tenant and action, the approved set, the commit, the expiry, and the scheduler's fence, then acquires the issuance fence and runs the resolver again under it.

It accepts only an Exact result bound to the request's query bounds and returns InvalidApprovedResult for anything else. If the affected set, commit index, or hashes moved since approval, it releases the fence it acquired and returns ApprovalInvalidated; if that release fails, it returns PortFailure, and the operator retains and reconciles that fence. A fence-port failure stays a failure, so no path authorizes a capability suspension without a fence.

A one-node budget cannot certify the repository worker's lineage

The affected repository capability delegated to a dependency reviewer, which produced a retained read receipt; a separate indexing capability sits outside that lineage. This integration resolves the original query, resolves it again with a one-node budget, and exposes the fenced revalidation call.

The configured host supplies CausalLineageStore and CausalLineageFenceStore implementations with authenticated commit metadata. The original BlastRadiusRequest comes from the retained finding: tenant, action, the seed IDs, and explicit depth, node, and edge bounds. The scheduler owns the live lease and the LineageFenceRequest, so this code never manufactures that authority.

security-host/resolve_worker_lineage.rsrust
use chio_quarantine::{CausalBlastRadiusResolver, FenceValidationOutcome};
use chio_security_types::ports::{
    BlastRadiusFenceAcquisition, BlastRadiusQueryBounds, BlastRadiusRequest, BlastRadiusResult,
    CausalLineageFenceStore, CausalLineageStore, LineageFence, LineageFenceRequest,
};

pub struct LineageComparison {
    pub original: BlastRadiusResult,
    pub one_node_budget: BlastRadiusResult,
}

/// The request's tenant, action and seed belong to the retained finding.
/// Stores provide actual authoritative commit metadata and completeness.
pub fn resolve_original<S, F>(
    resolver: &CausalBlastRadiusResolver<S, F>,
    original: &BlastRadiusRequest,
) -> LineageComparison
where
    S: CausalLineageStore + ?Sized,
    F: CausalLineageFenceStore + ?Sized,
{
    let result = resolver.resolve(original);
    let mut narrowed = original.clone();
    narrowed.query_bounds = BlastRadiusQueryBounds {
        max_depth: original.query_bounds.max_depth,
        max_nodes: 1,
        max_edges: original.query_bounds.max_edges,
    };
    let one_node_budget = resolver.resolve(&narrowed);
    LineageComparison {
        original: result,
        one_node_budget,
    }
}

/// The scheduler supplies its live lease/fencing identity and exact approved
/// commitment. This rechecks the same graph under the acquired issuance fence.
pub fn fence_original<S, F>(
    resolver: &CausalBlastRadiusResolver<S, F>,
    retained_approval: &BlastRadiusFenceAcquisition,
    expected: &LineageFenceRequest,
) -> Result<LineageFence, FenceValidationOutcome>
where
    S: CausalLineageStore + ?Sized,
    F: CausalLineageFenceStore + ?Sized,
{
    resolver.acquire_validated_fence(
        &retained_approval.request,
        &retained_approval.approved_result,
        retained_approval.expires_at_unix_ms,
        expected,
    )
}

For the depicted graph, you should see an exact original result naming repository and dependency-review with its affected-set hash, graph-slice hash, source version, commit, and completeness watermark. The one-node result is Incomplete with its retained query bounds, because one node cannot cover the delegated child and its receipt. fence_original returns a fence only when the graph still matches the approved commitment.

An incomplete view never reaches containment

An Incomplete result has no affected set for a lineage-scoped plan to bind. ResponseStateMachine::create refuses with StateMachineError::InvalidPlan any plan whose FreezeIssuance effect lacks an Exact result matching the plan's affected set, and the fence step refuses an incomplete result again.

RefusalConditionRaised by
BlastRadiusResult::Incomplete, TruncatedSnapshot
The store reported a truncated snapshot
resolve, as a value
ResponseShapeError::MissingIssuanceFence
A lineage plan does not start with FreezeIssuance
ResponsePlan::validate_shape
KernelError::GovernedTransactionDenied
The same plan reaches the kernel
Kernel active-response admission
FenceValidationOutcome::InvalidApprovedResult
The approved result is incomplete or unbound
acquire_validated_fence
FenceValidationOutcome::ApprovalInvalidated
Set, commit, or hashes changed after approval
acquire_validated_fence

The shape error reads "lineage response does not begin with an issuance fence", and the kernel wraps "active-response authorization denied: lineage-scoped response plan does not begin with an issuance fence". Through BlastRadiusPort::acquire_fence, the two fence outcomes arrive as a PortError of kind InvalidData (store.invalid_data) and Conflict (store.conflict).

Each refusal names its repair. Recover a complete snapshot within supported bounds and keep automatic containment suppressed. Rebuild a misordered plan with the freeze first and approve that exact plan. For a fence refusal, resolve an exact result for the same request and bounds, or resolve and approve the refreshed lineage after confirming the stale fence was released. During recovery, inspect the original action, set commitment, and live fence before continuing; a narrower session response needs its own established target and reviewed authority.

Conformance coverage

The conformance test truncated_lineage_no_containment drives a lineage store that reports truncation and asserts that the resolver returns Incomplete with TruncatedSnapshot and that acquire_validated_fence refuses that result with InvalidApprovedResult. The active-defense-conformance gate runs that suite. Every Chio release passes the active-defense-conformance gate before it is tagged. chio release verify --evidence qualification shows its result for the release you run.

Next: authorize the plan that binds this set