hessra_context_token/lib.rs
1//! # Hessra Context Token
2//!
3//! Context token implementation for information flow control (exposure tracking)
4//! in the Hessra authorization system.
5//!
6//! Context tokens track what data an object (typically an AI agent) has been
7//! exposed to during a session. Each data access adds exposure labels as
8//! append-only biscuit blocks, which downstream systems use to restrict
9//! available capabilities.
10//!
11//! ## Key Properties
12//!
13//! - **Append-only**: Exposure labels accumulate and cannot be removed within a session.
14//! - **Reject-based enforcement**: Each label is recorded as a `reject if exposure({label})`
15//! rule. A verifier asserts the labels it cares about as `exposure({label})` facts; a
16//! matching reject rule fails authorization. Reject rules are monotonic and apply to the
17//! whole token regardless of which key signed them -- any party (even an ephemeral key)
18//! can tighten a token, and no `trusting` scope is needed on the authz path.
19//! - **Enumerable**: Each label is also recorded as an `exposed_label({label})` metadata
20//! fact (a separate predicate the reject rules never test), queried with
21//! `trusting authority, {pubkey}` so only issuer-attested labels are reported.
22//! - **Block-stacked**: All labels added in a single call land in the same block.
23//! - **Inheritable**: Child contexts inherit parent exposure via `fork_context`.
24//!
25//! ## Authority Block
26//!
27//! ```datalog
28//! context(subject);
29//! check if time($time), $time < expiration;
30//! // optional, only if mint-time exposures supplied:
31//! reject if exposure(label_1);
32//! exposed_label(label_1);
33//! reject if exposure(label_2);
34//! exposed_label(label_2);
35//! exposure_source(source);
36//! exposure_time(timestamp);
37//! ```
38//!
39//! ## Third-party exposure blocks (one per `add_exposure` call)
40//!
41//! ```datalog
42//! reject if exposure(label_a);
43//! exposed_label(label_a);
44//! reject if exposure(label_b);
45//! exposed_label(label_b);
46//! exposure_source(source);
47//! exposure_time(timestamp);
48//! ```
49//!
50//! ## Example
51//!
52//! ```rust
53//! use hessra_context_token::{HessraContext, ContextVerifier, add_exposure};
54//! use hessra_token_core::{KeyPair, TokenTimeConfig};
55//!
56//! let keypair = KeyPair::new();
57//! let public_key = keypair.public();
58//!
59//! // Mint a fresh context token
60//! let token = HessraContext::new("agent:openclaw".to_string(), TokenTimeConfig::default())
61//! .issue(&keypair)
62//! .expect("Failed to create context token");
63//!
64//! // Add exposure labels (stacked into one third-party block, signed by the issuer)
65//! let exposed = add_exposure(
66//! &token,
67//! &keypair,
68//! &["PII:SSN".to_string()],
69//! "data:user-ssn".to_string(),
70//! ).expect("Failed to add exposure");
71//!
72//! // Verify with chained exclusion checks
73//! ContextVerifier::new(exposed.clone(), public_key)
74//! .excludes("PII:email")
75//! .verify()
76//! .expect("PII:email is not attested");
77//!
78//! assert!(ContextVerifier::new(exposed, public_key)
79//! .excludes("PII:SSN")
80//! .verify()
81//! .is_err());
82//! ```
83
84mod exposure;
85mod inspect;
86mod mint;
87mod verify;
88
89pub use exposure::{add_exposure, extract_exposure_labels, fork_context};
90pub use inspect::{ContextInspectResult, inspect_context_token};
91pub use mint::HessraContext;
92pub use verify::ContextVerifier;
93
94// Re-export commonly needed types from core
95pub use hessra_token_core::{
96 Biscuit, KeyPair, PublicKey, TokenError, TokenTimeConfig, decode_token, encode_token,
97 parse_token, public_key_from_pem_file,
98};