Skip to main content

Session

Struct Session 

Source
pub struct Session { /* private fields */ }
Expand description

Owns the token manifest for one conversation or request.

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.

§Restore workflow

  1. Call Pipeline::redact - returns a gaze_types::CleanDocument and updates the session map.
  2. Call Session::export to produce a SensitiveSnapshot.
  3. Persist the raw bytes (snapshot.into_bytes()) encrypted at rest - they contain original PII.
  4. Send only the cleaned text to the LLM.
  5. After the LLM responds, call Session::import with SensitiveSnapshot::from(bytes).
  6. For each token in the LLM response, call Session::restore_strict (or Session::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)

Implementations§

Source§

impl Session

Source

pub fn new(scope: Scope) -> Result<Self>

Source

pub fn from_policy(policy: &Policy) -> Result<Self>

Source

pub fn from_policy_with_ttl_override( policy: &Policy, ttl_secs_override: Option<u64>, ) -> Result<Self>

Source

pub fn tokenize(&self, class: &PiiClass, raw: &str) -> Result<String>

Source

pub fn tokenize_with_family( &self, family: &str, class: &PiiClass, raw: &str, ) -> Result<String>

Source

pub fn format_preserving_fake( &self, class: &PiiClass, raw: &str, ) -> Result<String>

Source

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.

Source

pub fn contains_token(&self, token: &str) -> bool

Source

pub fn session_hex(&self) -> String

Source

pub fn audit_session_id(&self) -> &str

Source

pub fn restore_strict(&self, token: &str) -> Result<String>

Source

pub fn restore(&self, token: &str) -> Option<String>

Source

pub fn export(&self) -> Result<SensitiveSnapshot>

Source

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/architecture/document-extension.md.

Source

pub fn import(snapshot: SensitiveSnapshot) -> Result<Self>

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more