pub struct Session { /* private fields */ }Expand description
Owns the token manifest for one pseudonym namespace.
A Session holds the bidirectional map between PII values and their pseudonymous tokens.
Create one per conversation and thread it through every Pipeline::redact call.
A Session is the boundary of a pseudonym namespace. Each new Session
starts with fresh per-class counters (Person_1, Email_1, etc.) and a
fresh session_hex prefix. Two Sessions never share next_by_class,
token_by_value, or value_by_token, regardless of Scope variant.
The Scope variant chooses persistence, not isolation:
Scope::Ephemeralis process-bound. Its namespace lives until the Session is dropped. Use it for ad-hoc one-off redaction. Do not share oneEphemeralSession across logical conversations if those conversations should have independent counters and value-to-token mappings.Scope::Conversation(String)is a keyed namespace that can be re-opened across process restarts when the adopter stores and imports the session snapshot. Use it for per-conversation chat or per-thread agents.Scope::Persistent { .. }is a long-lived namespace with a TTL encoded in exported snapshots.
Picking the wrong variant does not break single-call redaction correctness, but it can produce cross-call linkability: identical raw values fed into one Session always map to the same pseudonym, so downstream consumers can link mentions across contexts that the adopter intended to keep independent.
§Restore workflow
- Call
Pipeline::redact- returns agaze_types::CleanDocumentand updates the session map. - Call
Session::exportto produce aSensitiveSnapshot. - Persist the raw bytes (
snapshot.into_bytes()) encrypted at rest - they contain original PII. - Send only the cleaned text to the LLM.
- After the LLM responds, call
Session::importwithSensitiveSnapshot::from(bytes). - For each token in the LLM response, call
Session::restore_strict(orSession::restore).
There is no Pipeline::restore_text method - full-text restore is performed by scanning tokens
with crate::token_shape::pattern and calling restore_strict per token.
Document workflows use the same restore root. Call Session::export_with_extension only when
writing a gaze-document bundle that needs signed integrity hashes and codec provenance; plain
text adopters should keep using Session::export.
§Round-trip example
use gaze::{
token_shape, Action, ClassRule, CleanDocument, DefaultRule, Detection, Detector, PiiClass,
Pipeline, RawDocument, Scope, SensitiveSnapshot, Session,
};
struct ExampleEmailDetector;
impl Detector for ExampleEmailDetector {
fn detect(&self, input: &str) -> Vec<Detection> {
let email = "alice@example.invalid"; // fixture-cited(crates/gaze/src/session.rs:session::tests::snapshot_round_trip_two_families_same_class_raw_preserved_under_shared_counter)
input
.find(email)
.map(|start| Detection::new(start..start + email.len(), PiiClass::Email, "docs"))
.into_iter()
.collect()
}
}
let pipeline = Pipeline::builder()
.detector(ExampleEmailDetector)
.rule(ClassRule::new(PiiClass::Email, Action::Tokenize))
.rule(DefaultRule::new(Action::Preserve))
.build()?;
let session = Session::new(Scope::Conversation("conv-1".into()))?;
let CleanDocument::Text(clean) = pipeline.redact(
&session,
RawDocument::Text("alice@example.invalid".into()), // fixture-cited(crates/gaze/src/session.rs:session::tests::snapshot_round_trip_two_families_same_class_raw_preserved_under_shared_counter)
)? else {
panic!("text variant expected");
};
// Export before sending clean text to the LLM. Persist `blob` encrypted at rest.
let snapshot = session.export()?;
let blob: Vec<u8> = snapshot.into_bytes();
// Restore on the owner side after the LLM responds.
let restored_session = Session::import(SensitiveSnapshot::from(blob))?;
let mut restored = String::new();
let mut last = 0;
for m in token_shape::pattern().find_iter(&clean) {
restored.push_str(&clean[last..m.start()]);
restored.push_str(&restored_session.restore_strict(m.as_str())?);
last = m.end();
}
restored.push_str(&clean[last..]);
assert_eq!(restored, "alice@example.invalid"); // fixture-cited(crates/gaze/src/session.rs:session::tests::snapshot_round_trip_two_families_same_class_raw_preserved_under_shared_counter)See: docs/explanation/core/session-contract.md for the full architecture contract.
Implementations§
Source§impl Session
impl Session
pub fn new(scope: Scope) -> Result<Self>
pub fn from_policy(policy: &Policy) -> Result<Self>
pub fn from_policy_with_ttl_override( policy: &Policy, ttl_secs_override: Option<u64>, ) -> Result<Self>
pub fn tokenize(&self, class: &PiiClass, raw: &str) -> Result<String>
pub fn tokenize_with_family( &self, family: &str, class: &PiiClass, raw: &str, ) -> Result<String>
pub fn format_preserving_fake( &self, class: &PiiClass, raw: &str, ) -> Result<String>
Sourcepub fn tokens(&self) -> Vec<String>
pub fn tokens(&self) -> Vec<String>
Enumerate every live token string emitted by this session.
Intended for restore-side callers that need to build an exact-literal
alternation regex over the session map (Pass 1 of the two-pass restore
strategy): replacing token-shaped strings via
a class-shape regex alone is unsafe because it either (a) straddles
word boundaries into adjacent text, or (b) misses lowercase
FormatPreserve shapes like location_1. Feeding these exact strings
into regex::escape and sorting longest-first avoids both pitfalls.
Returned order is unspecified — callers that rely on longest-first matching must sort the returned vector themselves.
pub fn contains_token(&self, token: &str) -> bool
pub fn session_hex(&self) -> String
pub fn audit_session_id(&self) -> &str
pub fn snapshot_entries(&self) -> Vec<SessionSnapshotEntry>
pub fn restore_strict(&self, token: &str) -> Result<String>
pub fn restore_strict_text(&self, text: &str) -> Result<String, RestoreError>
pub fn restore_strict_text_with_events( &self, text: &str, ) -> Result<(String, Vec<RestoreEvent>), RestoreError>
pub fn restore_boundary_events(&self, text: &str) -> Vec<RestoreEvent>
pub fn restore(&self, token: &str) -> Option<String>
pub fn export(&self) -> Result<SensitiveSnapshot>
Sourcepub fn export_with_extension(
&self,
extension: DocumentExtension,
) -> Result<SensitiveSnapshot>
pub fn export_with_extension( &self, extension: DocumentExtension, ) -> Result<SensitiveSnapshot>
Export a document-extended snapshot for gaze-document bundle manifests.
Use this instead of Session::export when writing a document bundle with
<base>-agent/ files (clean.md, layout.json, report.json, optional
preview-redacted.png) plus owner-only <base>-owner/manifest.bin. The supplied
DocumentExtension is serialized inside the signed snapshot payload, so its hashes and
codec audit rows become the integrity root for the agent-facing files. Text-only adopters
should use Session::export. Current snapshots emit v5 so older readers fail closed
before restore while the signature binds the emitted envelope bytes.
use gaze::{
CodecAuditRow, CodecCapabilitySet, DocumentExtension, ExtractionDensityPolicy, Scope,
Session, TextOrigin,
};
let session = Session::new(Scope::Conversation("doc-1".to_string()))?;
let mut codec = CodecAuditRow::new(
"gaze.codec.pdf",
"0.7.0",
"application/pdf",
TextOrigin::Hybrid,
);
codec.advertised = CodecCapabilitySet::new(true, true, true, false);
codec.delivered = CodecCapabilitySet::new(true, true, false, false);
codec.extraction_density_policy = ExtractionDensityPolicy::Required(1.0);
let extension = DocumentExtension::builder(1)
.clean_md_sha256([1; 32])
.layout_json_sha256([2; 32])
.report_json_sha256([3; 32])
.page_count(4)
.audit_session_id(session.audit_session_id())
.codec_audit(vec![codec])
.build()?;
let manifest_bin = session.export_with_extension(extension)?.into_bytes();Failure modes match Session::export: ephemeral sessions return
Error::ExportForbidden, JSON encoding failures return Error::SnapshotDecode, and
empty integrity-binding hashes or audit session ids return
Error::EmptyDocumentIntegrity. page_count == 0 is allowed because text-only or
degenerate bundles may have no pages while still binding file hashes. Callers must treat the
returned SensitiveSnapshot as owner-only because it carries the full token-to-PII restore
map. Bundle layout details live in docs/explanation/document/document-extension.md.
pub fn import(snapshot: SensitiveSnapshot) -> Result<Self>
Auto Trait Implementations§
impl !Freeze for Session
impl !RefUnwindSafe for Session
impl Send for Session
impl Sync for Session
impl Unpin for Session
impl UnsafeUnpin for Session
impl UnwindSafe for Session
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more