Capabilities as Schubert Conditions

Every capability in Schubert is a Schubert condition — a geometric constraint with a partition, a kind, and a label.

Defining a Capability

#![allow(unused)]
fn main() {
use schubert::{Capability, CapabilityKind};

let read = Capability::new(
    "read:data",        // unique ID
    "Read data access", // human-readable label
    vec![1],            // partition: σ₁ (codimension 1)
    CapabilityKind::ReadLike,
);

let write = Capability::new(
    "write:data",
    "Write data access",
    vec![2],             // partition: σ₂ (codimension 2)
    CapabilityKind::WriteLike,
);

let admin = Capability::new(
    "admin",
    "Full administrative access",
    vec![2, 2],          // partition: σ₂₂ (codimension 4, point class)
    CapabilityKind::AdminLike,
);
}

CapabilityKind

The CapabilityKind affects how capabilities behave under trust degradation:

KindTrust SensitivityExamples
ReadLikeLow — stable even at low trustRead, list, view
WriteLikeMedium — degrades at moderate trustWrite, update, delete
AdminLikeHigh — degrades rapidly under trust lossAdmin, manage, configure
CustomApplication-definedAny custom semantic

Higher-codimension AdminLike capabilities are the first to become unstable as trust erodes. This models the real-world principle that powerful capabilities should require more trust.

Partition Design

Partitions determine how capabilities interact. Key rules:

  • Partitions must be weakly decreasing: [2,1] is valid, [1,2] is not
  • Codimension = sum of entries: [2,1] has codimension 3
  • One element = one row: [1] is a single-row condition
  • Equal partitions = same restriction: two capabilities with [1] are equivalent in their geometric constraint

Temporal Capabilities

Capabilities can have an expiry time:

#![allow(unused)]
fn main() {
let temp = Capability::new("temp", "Temporary access", vec![1], CapabilityKind::ReadLike)
    .with_expiry(now + 3_600_000); // 1 hour from now

acl.register_capability(temp)?;
acl.grant(&principal, "temp")?;

// Later:
acl.check_temporal(&principal, &["temp"], now)?;       // OK
acl.check_temporal(&principal, &["temp"], later)?;      // Denied
}

Registration and Grants

Capabilities must be registered with the controller before they can be granted:

#![allow(unused)]
fn main() {
// 1. Register
acl.register_capability(read)?;

// 2. Grant to principal
acl.grant(&alice, "read:data")?;

// 3. Check
acl.check(&alice, &["read:data"])?;
}

Registration defines the capability's geometry. Granting assigns it to a principal. Checking evaluates the intersection.