Access Decisions

When you call acl.check(), Schubert returns an AccessDecision — not a boolean, but a quantitative result with four variants.

The Four Decisions

#![allow(unused)]
fn main() {
pub enum AccessDecision {
    /// Access allowed with exactly this many configurations
    Granted { configurations: usize },
    /// Conditions are geometrically incompatible (Littlewood-Richardson = 0)
    Impossible { conflicting: Vec<String> },
    /// Too many conditions for the policy space (overconstrained)
    Denied,
    /// Too few conditions — policy is loose
    Underconstrained { dimension: usize },
}
}

Granted

The intersection number is positive. The principal can access the resource in configurations distinct ways.

#![allow(unused)]
fn main() {
acl.grant(&alice, "read")?;
acl.grant(&alice, "write")?;

let result = acl.check(&alice, &["read", "write"])?;
match result {
    AccessDecision::Granted { configurations } => {
        // configurations is the Littlewood-Richardson coefficient
        // for σ₁ ∩ σ₂ in this Grassmannian
    }
    _ => {}
}
}

Impossible

The killer feature. Individual capabilities are valid but together they're geometrically impossible. A traditional boolean AND would approve.

#![allow(unused)]
fn main() {
// σ₂ (write) and σ₁₁ (internal audit) in Gr(2,4)
acl.grant(&principal, "write")?;
acl.grant(&principal, "internal_audit")?;

let result = acl.check(&principal, &["write", "internal_audit"])?;
// AccessDecision::Impossible { conflicting: ["write", "internal_audit"] }
}

The Littlewood-Richardson coefficient σ₂ · σ₁₁ = 0 in Gr(2,4). No subspace can simultaneously satisfy both conditions.

Denied

Too many independent conditions — the intersection is empty because the total codimension exceeds the Grassmannian dimension.

#![allow(unused)]
fn main() {
// Gr(2,4) has dimension 4 — can't impose 5 independent conditions
acl.check(&principal, &["c1", "c2", "c3", "c4", "c5"])?;
// AccessDecision::Denied
}

Underconstrained

Too few conditions — the policy doesn't pin down a specific configuration. The remaining dimension tells you how loose the policy is.

#![allow(unused)]
fn main() {
// Only one condition in Gr(2,4) with dimension 4
// Remaining dimension = 4 - 1 = 3
acl.check(&principal, &["read"])?;
// AccessDecision::Underconstrained { dimension: 3 }
}

Computation Paths

Schubert supports four computation engines for computing intersection numbers:

PathWhen to use
LRDefault — balanced performance, exact results
LocalizationWhen you need geometric insight into why
TropicalLarge-scale batch operations (>1000 principals)
MatroidWhen parallel evaluation is enabled
#![allow(unused)]
fn main() {
use schubert::ComputationPath;

acl.set_computation_path(ComputationPath::Tropical);
}