Skip to main content

portcullis/
lib.rs

1//! # Lattice Guard
2//!
3//! A quotient lattice for AI agent permissions that prevents uninhabitable states.
4//!
5//! ## The Uninhabitable State
6//!
7//! The [uninhabitable state](https://simonwillison.net/2025/Jun/16/the-lethal-trifecta/)
8//! (originally termed "lethal trifecta" by Simon Willison) describes
9//! three capabilities that, when combined in an AI agent, create critical security vulnerabilities:
10//!
11//! 1. **Access to private data** - reading files, credentials, secrets
12//! 2. **Exposure to untrusted content** - web search, fetching URLs, processing external input
13//! 3. **External communication** - git push, PR creation, API calls, command execution
14//!
15//! When an agent has all three at autonomous levels, prompt injection attacks can exfiltrate
16//! private data without human oversight.
17//!
18//! ## Solution: Quotient Lattice
19//!
20//! This crate models permissions as a product lattice L with a **nucleus** operator
21//! that projects onto the quotient lattice L' of safe configurations:
22//!
23//! ```text
24//! L  = Capabilities × Paths × Budget × Commands × Time
25//! L' = { x ∈ L : ν(x) = x }  (safe configurations)
26//!
27//! The nucleus operator ν:
28//! • Is idempotent: ν(ν(x)) = ν(x)
29//! • Is deflationary: ν(x) ≤ x
30//! • Preserves meets: ν(x ∧ y) = ν(x) ∧ ν(y)
31//! ```
32//!
33//! When the uninhabitable_state is detected, exfiltration operations gain approval
34//! obligations. The quotient L' contains only configurations where this
35//! invariant holds.
36//!
37//! ## Quick Start
38//!
39//! ```rust
40//! use portcullis::{Operation, PermissionLattice, CapabilityLevel};
41//!
42//! // Create a permission set with dangerous capabilities
43//! let mut perms = PermissionLattice::default();
44//! perms.capabilities.read_files = CapabilityLevel::Always;    // Private data
45//! perms.capabilities.web_fetch = CapabilityLevel::LowRisk;    // Untrusted content
46//! perms.capabilities.git_push = CapabilityLevel::LowRisk;     // Exfiltration
47//!
48//! // The meet operation detects the uninhabitable_state and adds approval obligations
49//! let safe = perms.meet(&perms);
50//! assert!(safe.requires_approval(Operation::GitPush));
51//! ```
52//!
53//! ## Integration with Claude Code / OpenClaw
54//!
55//! See the `examples/` directory for integration patterns with popular AI agent frameworks.
56//!
57//! ## Security Model
58//!
59//! See `THREAT_MODEL.md` for a complete description of what this crate prevents
60//! and what it does not prevent.
61//!
62//! ## User-Defined Policies (CEL)
63//!
64//! With the `cel` feature enabled, you can define policies with CEL constraints:
65//!
66//! ```rust,ignore
67//! use portcullis::constraint::{Constraint, Policy, PolicyContext};
68//! use portcullis::frame::Nucleus;
69//! use portcullis::{Operation, PermissionLattice};
70//!
71//! // Create a policy with custom constraints
72//! let policy = Policy::new("secure-workspace")
73//!     .with_constraint(
74//!         Constraint::new(
75//!             "workspace-only",
76//!             r#"operation == "write_files" && !path.startsWith("/workspace/")"#,
77//!         )?.with_obligation(Operation::WriteFiles)
78//!     );
79//!
80//! // Apply the policy as a nucleus
81//! let perms = PermissionLattice::permissive();
82//! let safe = policy.apply(&perms);
83//! ```
84
85#![deny(missing_docs)]
86#![deny(unsafe_code)]
87
88pub mod audit;
89#[cfg(feature = "serde")]
90pub mod audit_backend;
91mod budget;
92mod capability;
93#[cfg(feature = "crypto")]
94pub mod certificate;
95mod command;
96pub mod constraint;
97pub mod uninhabitable_state;
98
99pub mod delegation;
100pub mod dropout;
101pub mod escalation;
102pub mod exposure_core;
103pub mod frame;
104pub mod galois;
105pub mod graded;
106pub mod guard;
107pub mod heyting;
108/// Kernel decision engine — complete mediation with monotone session state.
109#[cfg(all(feature = "serde", feature = "crypto"))]
110pub mod kernel;
111/// MCP mediation: classify and gate arbitrary MCP tool calls against the
112/// permission lattice with exposure tracking.
113///
114/// Requires the `spec` feature (includes `serde`, `serde_yaml`, `toml`).
115#[cfg(feature = "spec")]
116pub mod mcp_mediation;
117/// Progressive discovery: observe agent behavior and generate minimal policies.
118///
119/// Requires the `spec` feature (includes `serde`, `serde_yaml`, `toml`).
120#[cfg(feature = "spec")]
121pub mod observe;
122/// Declarative profile specification and canonical profile registry.
123///
124/// Requires the `spec` feature (includes `serde`, `cel`, `serde_yaml`, `toml`).
125#[cfg(feature = "spec")]
126pub mod profile;
127#[cfg(feature = "remote-audit")]
128pub mod s3_audit_backend;
129/// Attenuation tokens — compact delegation credentials for wire transport.
130///
131/// Requires the `serde` feature for serialization.
132#[cfg(feature = "crypto")]
133pub mod token;
134
135pub mod identity;
136pub mod intent;
137pub mod isolation;
138mod lattice;
139pub mod metrics;
140pub mod modal;
141mod path;
142pub mod permissive;
143pub mod pipeline;
144pub mod progress;
145pub mod region;
146mod time;
147pub mod trust;
148pub mod weakening;
149pub mod workspace;
150
151#[cfg(kani)]
152mod kani;
153
154pub use budget::BudgetLattice;
155pub use capability::{
156    CapabilityLattice, CapabilityLevel, ExtensionOperation, IncompatibilityConstraint, Obligations,
157    Operation, StateRisk,
158};
159pub use command::{ArgPattern, CommandLattice, CommandPattern};
160pub use exposure_core::{apply_record, classify_operation, project_exposure, should_deny};
161pub use frame::{
162    verify_nucleus_laws, BoundedLattice, CompleteLattice, ComposedNucleus, DistributiveLattice,
163    Frame, Lattice, Nucleus, NucleusLaw, NucleusLawViolation, SafePermissionLattice,
164    UninhabitableQuotient,
165};
166pub use galois::{
167    Composable, GaloisConnection, GaloisVerificationError, TranslationReport, TranslationStep,
168    TrustDomainBridge,
169};
170pub use graded::{Graded, GradedPermissionCheck, GradedPipeline, RiskCost, RiskGrade};
171#[allow(deprecated)]
172pub use guard::{
173    operation_exposure, CheckProof, CompositeGuard, ExecuteError, ExposureLabel, ExposureSet,
174    ExtensionExposureLabel, GradedExposureGuard, GradedGuard, GuardError, GuardFn, GuardedAction,
175    PermissionGuard, RuntimeStateGuard, ToolCallGuard,
176};
177pub use heyting::{ConditionalPermission, HeytingAlgebra};
178pub use intent::{IntentKind, WorkIntent};
179pub use isolation::{FileIsolation, IsolationLattice, NetworkIsolation, ProcessIsolation};
180pub use lattice::{
181    DelegationError, EffectivePermissions, PermissionLattice, PermissionLatticeBuilder,
182};
183pub use modal::{CapabilityModal, EscalationPath, EscalationStep, ModalContext, ModalPermissions};
184pub use path::PathLattice;
185pub use permissive::{
186    ExecutionDenied, PermissiveExecution, PermissiveExecutionResult, PermissiveExecutor,
187    PermissiveExecutorBuilder,
188};
189pub use progress::{ProgressDimension, ProgressLattice, ProgressLevel};
190pub use region::CodeRegion;
191pub use time::TimeLattice;
192pub use trust::{EnforcementResult, TrustProfile};
193pub use weakening::{
194    WeakeningCost, WeakeningCostConfig, WeakeningDimension, WeakeningGap, WeakeningRequest,
195};
196pub use workspace::WorkspaceGuard;
197
198// Re-export pipeline types
199pub use pipeline::{
200    algebraic_gap, evaluate_and_escalate, full_pipeline, justify_necessity, require_or_escalate,
201    translate_with_cost, AlgebraicWeakeningGap, CostAnnotatedTranslation, EscalationTrigger,
202    HopCost, IntentRegionMapping, ModalJustification, ModalJustificationEntry, PipelineTrace,
203    RiskEvaluation,
204};
205
206// Re-export key audit and metrics types
207pub use audit::{
208    AuditEntry, AuditLog, ChainVerificationError, IdentityAuditSummary, PermissionEvent,
209    RetentionPolicy,
210};
211#[cfg(feature = "crypto")]
212pub use certificate::{
213    canonical_permissions_hash, verify_certificate, CertificateDelegationError, CertificateError,
214    LatticeCertificate, VerifiedPermissions,
215};
216pub use delegation::{
217    meet_with_justification, DelegationChain, DelegationLink, MeetJustification, RestrictionDetail,
218    RestrictionReason,
219};
220pub use metrics::{
221    build_deviation_report, DeviationDetail, DeviationReport, InMemoryMetrics, MetricEvent,
222    MetricsCollector, MetricsReport, ReputationMetrics, ReputationWeights,
223};
224#[cfg(feature = "crypto")]
225pub use token::{AttenuationToken, SessionProvenance, TokenError};
226pub use uninhabitable_state::{ConstraintNucleus, CoreExposureRequirement, UninhabitableState};
227
228/// Check if a glob pattern matches a path.
229pub fn glob_match(pattern: &str, path: &str) -> bool {
230    path::glob_match(pattern, path)
231}